solar panel watchdog

Topics (not sure which fora)
when not sure where to post, post here and mods will move it to right forum.

Moderators: leecollings, remb0

Post Reply
renerene
Posts: 356
Joined: Wednesday 03 August 2016 11:36
Target OS: -
Domoticz version:
Contact:

solar panel watchdog

Post by renerene »

Is my solar panel still working as it should? Would be nice to have a domoticz watchdog routine that checks if erveything is still ok on my roof.
I'm using the Growatt plugin, so there's already information how much power is prodcuced.
Simple method for the watchdog would be: check if there has been power produced last 24 hour. If not: notify user.
More elegant method would be: gather lux data from metro service (Weatherunderground) and compare with daily produced solar power.

Before I start programming: anyone here with advice?
willemd
Posts: 739
Joined: Saturday 21 September 2019 17:55
Target OS: Raspberry Pi / ODroid
Domoticz version: 2024.1
Location: The Netherlands
Contact:

Re: solar panel watchdog

Post by willemd »

Here is a script that I use to calculate a theoretical PV panel production, based on weather data (in particular sun power measured at a nearby weather station to determine sky cover), sun position and PV panel placement. This is then compared to the actual PV panel production.

This is based on work of others for calculation of sun azimuth and altitude etc, as published in this forum, and has been modified further with calculation of solar irradiance on the PV panels.

The final results are also made visible in Dashticz so it is easy to compare and spot anomalies.

Code: Select all

--[[
This script calculate in real-time some usefull solar data without any hardware sensor :
·  Azimuth : Angle between the Sun and the north, in degree. (north vector and the perpendicular projection of the sun down onto the horizon)
·  Altitude : Angle of the Sun with the horizon, in degree.


-- Installation & Documentation -----------------------------------------------------------
https://www.domoticz.com/wiki/index.php?title=Lua_dzVents_-_Solar_Data:_Azimuth_Altitude_Lux

-- Prerequisits  -----------------------------------------------------------
	Requires Domoticz v3.8551 or later.   
	Work with lua 5.3 too.
	No Platform dependent; Linux & Windows


-- Contributors  ----------------------------------------------------------------
	V1.0  - Sébastien Joly - Great original work
	V1.1  - Neutrino - Adaptation to Domoticz
	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  - Jmleglise - 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 Domoticz that doesn't catch the devicechanged event of the virtual sensor)
	V1.5  - xces - UTC time calculation.
	      - waaren - pow function for lua 5.3 compatibility
	V2.4  - BakSeeDaa - Converted to dzVents and changed quite many things.
	V2.41 - Oredin - Use Dark Sky API instead of WU API
	V3.0  - Bram Vreugdenhil/Hestia - Weather Api independent. Use OpenWeathermaps devices or your own sensors
	V3.1  - Jmleglise - Merge the different fork. Some clean Up, comment and wiki.
]]
--

-- Variables to customize ------------------------------------------------

-- Devices for Input Data
local idxPVpanel = 3  -- actual PVpanel total production, measured by homewizard kWh meter
local idxTempHumBaro = 4 -- barometer device, imported from buienradar API (KNMI weather station Hoek van Holland) 
local idxWind = 7
local idxSunpower = 8 -- sunpower as provided by buienradar, measured by pyranonmeter, so it is GHI, i.e. total radiation on horizontal surface

-- Devices for Output Data  (can be nil if you dont want some data)------------------------------------------
local idxSolarAltitude = 23    -- Virtual custom sensor for Solar Altitude (angle with horizon)
local idxSolarAzimuth  = 24    -- Virtual custom sensor for Solar Azimuth (angle versus north)
local idxAOI           = 25    -- Virtual custom sensor for AOI, angle of incidence of sunbeam on PVpanel
local idxClarityIndex  = 26     -- Virtual sensor for clarity = actualGHI/theoreticalGHI
local idxTheoreticalGHI   = 27
local idxActualDHI = 28 
local idxActualDNI = 29 
local idxIrradianceBeam = 30 
local idxIrradianceGround = 31
local idxIrradianceSky = 32
local idxIrradianceTotal = 33
local idxTheoreticalProduction = 34 
local idxRatioActTheorProduction = 35 
local idxRatioActMaxProduction = 36

-- Other parameters -----------------------------------------------------
local intervalMins = 5	    -- The interval of running this script. No need to be faster than the data source. (For example it is 10 min)
local altitude  = 0            -- Meters above sea level of your location. (Integer)  Can be found from coordinates on https://www.advancedconverter.com/map-tools/find-altitude-by-coordinates
local latitude  = nil 		    -- Keep nil if you have defined your lat. and long. in the settings. Otherwise you can overwrite it here. E.g. something like 51.748485
local longitude = nil 		    -- idem. E.g.something like 5.629728.
local logToFile = false		  			-- Set to true if you also want to log to a file. It might get big by time. 
local tmpLogFile = '/tmp/logSun.txt'	-- Logging to a file if specified 
local PVpanelTiltAngle = 55     -- Angle of PV panels versus horizontal (tilt angle, 0 is horizontal, 90 is vertical)
local PVpanelAzimuth = 238  -- Azimuth of PVpanel , 238 is almost exactly Sout-West direction

return {
	active = true,
	logging = {
        level = domoticz.LOG_INFO,                                     -- Uncomment to override the dzVents global logging setting
		marker = 'solar data'
	},
	on = {
--		devices = {'testSwitch'},                                       -- a switch for testing w/o waiting minutes
		timer = {'every '..tostring(intervalMins)..' minutes between sunrise and 30 minutes after sunset'}       -- There is no more limit to worry about as there is no API called
	},

	execute = function(domoticz, device)

		local function leapYear(year)   -- function to determine whether year is leapyear, true or false
			return year%4==0 and (year%100~=0 or year%400==0)
		end

		function math.pow(x, y)        -- Function math.pow(x, y) has been deprecated in Lua 5.3. therefore defined here
			return x^y 
		end		
		
		-- pick up location coordinates from domoticz settings
        if latitude == nil then
            latitude = domoticz.settings.location.latitude
        end
    
        if longitude == nil then
            longitude = domoticz.settings.location.longitude
        end		
		
        -- base solar data calculations
        -- reference http://www.plevenon-meteo.info/technique/theorie/enso/ensoleillement.html
        local year = os.date('%Y')		
		local nbDaysInYear = (leapYear(year) and 366 or 365)  -- total number of days in current year
		local numOfDay = os.date('%j')   -- number of current day in the year
		local angularSpeed = 360/365.25
		local timeDecimal = (os.date('!%H') + os.date('!%M') / 60 -25/60)      -- Coordinated Universal Time  (UTC)
		-- note a 25 minute time shift is applied because buienradar sunpower measurement is on average 25 minutes delayed
		local solarHour = timeDecimal + (4 * longitude / 60 )           -- The solar hour, time with correction for longitude, approx.
		local hourAngle = 15 * ( 12 - solarHour )                       -- angle of position of sun in its orbit, solar noon is 0
		local declination = math.deg(math.asin(0.3978 * math.sin(math.rad(angularSpeed) *(numOfDay - (81 - 2 * math.sin((math.rad(angularSpeed) * (numOfDay - 2))))))))
		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(hourAngle))))-- the height of the sun in degree, compared with the horizon
		local sunZenith = 90 - sunAltitude -- angle versus vertical instead of horizontal
		local sunAzimuth = 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(hourAngle))) / math.cos(math.rad(sunAltitude))
		if(sinAzimuth<0) then sunAzimuth=360-sunAzimuth 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 IncidenceAngle = math.deg(math.acos(math.cos(math.rad(sunZenith))*math.cos(math.rad(PVpanelTiltAngle))+math.sin(math.rad(sunZenith))*math.sin(math.rad(PVpanelTiltAngle))*math.cos(math.rad(sunAzimuth-PVpanelAzimuth))))  -- angle sunbeam on PVpanel
		
		domoticz.devices(idxAOI).updateCustomSensor(domoticz.utils.round(IncidenceAngle,0))
        domoticz.devices(idxSolarAzimuth).updateCustomSensor(domoticz.utils.round(sunAzimuth,0))
        domoticz.devices(idxSolarAltitude).updateCustomSensor(domoticz.utils.round(sunAltitude,0))
        
        
        
		if sunAltitude>5 then
    		-- first calculate theoretical maximum radiation on horizontal surface with clear sky
    		local constantSolarRadiation = 1367    -- Solar Constant W/m² produced by the sun, avg at entrance
    		-- influence of season : seasonRadiation=Solar radiation  (in W/m²) in the entrance of atmosphere, varying 3.4% depending on day of year (due to elliptical orbit around the sun)
    		local seasonRadiation = constantSolarRadiation * (1 +0.034 * math.cos( math.rad( 360 * numOfDay / nbDaysInYear ))) 
    		-- influence of atmosphere
    		local sinusSunAltitude = math.sin(math.rad(sunAltitude))  -- just for easy calculation, repititive factor		
    		local atmosphereFactor=1/(math.cos(math.rad(sunZenith))+0.50572*math.pow((96.07995-sunZenith),-1.6364)) -- pveducation.org
    		-- influence of altitude ; get barometer pressure from weather station
    		local relativePressure = domoticz.devices(idxTempHumBaro).barometer
    		local absolutePressure = relativePressure - domoticz.utils.round((altitude/ 8.3),1) -- hPa
    		local M = atmosphereFactor * relativePressure/absolutePressure -- influence of location altitude    
    		-- split Global Irradiance on horizontal surface in direct and diffuse factor, for a clear sky
    		local directNormalFactor=math.pow(0.7,math.pow(atmosphereFactor,0.678)) -- on a perpendicular surface
    		local directHorizontalFactor=directNormalFactor*sinusSunAltitude  -- if sunAltitude=90 then sunZenith=0 and sinusSunAltitude=1 therefore directHorizontalFactor=directNormalFactor
    		local diffuseFactor=(0.271 - 0.294 * math.pow(0.6,M)) * sinusSunAltitude
    		-- first calculate theoretical radiation on horizontal surface with a clear sky
    		local directRadiation, scatteredRadiation, totalRadiation
    		directRadiation = seasonRadiation * directHorizontalFactor
    		scatteredRadiation = seasonRadiation * diffuseFactor 
    		totalRadiation = scatteredRadiation + directRadiation
    		local theoreticalGHI=totalRadiation
    		
    		-- now compare theoretical GHI (=totalRadiation) to measured GHI (=sunpower=actualGHI)
    		-- pyranometer measurement by weather station, is actual global direct and indirect irradiance on horizontal surface (GHI)
    		local actualGHI = domoticz.devices(idxSunpower).sensorValue 
    		local clarityIndex = nil
    		if theoreticalGHI > 0 then
    		    clarityIndex=actualGHI/theoreticalGHI
    		end
    		
    		-- then split the actual GHI in components by using the clarityIndex
    		local diffuseFraction = 0
    		if clarityIndex <= 0.3 -- many clouds
            then
                diffuseFraction = 1.02 - 0.248 * clarityIndex  -- diffuse fraction high
            else 
                if (clarityIndex > 0.3 and clarityIndex < 0.78) 
                then
                    diffuseFraction = 1.45 - 1.67 * clarityIndex
                else -- light/no clouds
                    diffuseFraction = 0.147  -- diffuse fraction low
                end    
            end
            local actualDiffuseHorizontalIrradianceDHI = diffuseFraction * actualGHI
            local actualDirectNormalIrradianceDNI = (actualGHI - actualDiffuseHorizontalIrradianceDHI)/math.cos(math.rad(sunZenith))
        
    		-- and finally calculate the components for the solar panels
    		-- AOI = angle of incidence of sunlight on PV panels
            
            local directPVFactor=directNormalFactor*math.sin(math.rad(90-IncidenceAngle)) -- if AOI=0 then sinus(90-AOI)=1 therefore directHorizontalFactor=directNormalFactor
            local IrradianceBeam = 0
            local IrradianceGround = 0 
            local IrradianceSky = 0
            local IrradianceTotal = 0
            if sunAltitude>0
            then
                IrradianceBeam=actualDirectNormalIrradianceDNI * math.cos(math.rad(IncidenceAngle)) 
                if IrradianceBeam < 0 then  -- AOI >90, i.e. no beam directly on panel
                    IrradianceBeam = 0
                end    
                IrradianceGround=actualGHI * 0.2 * (1-math.cos(math.rad(PVpanelTiltAngle)))/2
                IrradianceSky=actualDiffuseHorizontalIrradianceDHI * (1+math.cos(math.rad(PVpanelTiltAngle)))/2 + actualGHI * ((0.012 * sunZenith - 0.04 ) * (1-math.cos(math.rad(PVpanelTiltAngle))))/2  -- should include DHI , soo too low
                IrradianceTotal=IrradianceBeam+IrradianceGround+IrradianceSky
            else
                IrradianceBeam = 0
                IrradianceGround = 0 
                IrradianceSky = 0
                IrradianceTotal = 0
            end    
            
            local RefractionIndex=(1-0.07*(1/math.cos(math.rad(IncidenceAngle)) - 1))
            if IncidenceAngle>90 or RefractionIndex<0 then
                RefractionIndex=0
            end    
            --print ('RefractionIndex',RefractionIndex)
            local Windspeed = domoticz.devices(idxWind).speedMs
            --print('windspeed ',Windspeed)
            local actualTemperature=domoticz.devices(idxTempHumBaro).temperature
            --print('temp ',actualTemperature)
            local PVTemperature=actualTemperature+IrradianceTotal*math.exp(-3.47-.0594*Windspeed)
            --print('pvtemp ',PVTemperature)
            local tempLossFactor=1
            if PVTemperature>25 then
                tempLossFactor= 1-(0.35 * (PVTemperature - 25))/100
            end        
            --print ('tempLossFactor ',tempLossFactor)        
           
            local PVpanelsize = 19.48 -- 12 x 1.64 m x 0.99 m total square Meters
            local PVpanelefficiency = 0.154 -- factory specified PVpanelefficiency
            local TheoreticalPVpanelProduction = PVpanelsize * PVpanelefficiency * IrradianceBeam *RefractionIndex * tempLossFactor + PVpanelsize * PVpanelefficiency * (IrradianceTotal - IrradianceBeam) * tempLossFactor -- RefractionIndex only applied to beam
            
            local ActualPVpanelProduction = domoticz.devices(idxPVpanel).actualWatt
            local RatioActualTheoreticalPVpanel=ActualPVpanelProduction / TheoreticalPVpanelProduction
       
            -- also compare actual to maximum production in case of clear sky
            local MaxBeam = 0
            local MaxGround = 0 
            local MaxSky = 0
            local MaxTotal = 0
            if sunAltitude>0
            then
                MaxBeam=directRadiation / math.cos(math.rad(sunZenith)) * math.cos(math.rad(IncidenceAngle)) 
                if MaxBeam < 0 then  -- AOI >90, i.e. no beam directly on panel
                    MaxBeam = 0
                end    
                MaxGround=theoreticalGHI * 0.2 * (1-math.cos(math.rad(PVpanelTiltAngle)))/2
                MaxSky=scatteredRadiation * (1+math.cos(math.rad(PVpanelTiltAngle)))/2 + theoreticalGHI * ((0.012 * sunZenith - 0.04 ) * (1-math.cos(math.rad(PVpanelTiltAngle))))/2  -- should include DHI , soo too low
                MaxTotal=MaxBeam+MaxGround+MaxSky
            else
                MaxBeam = 0
                MaxGround = 0 
                MaxSky = 0
                MaxTotal = 0
            end    
            --print('directRadiation ',directRadiation)
            --print('MaxBeam ',MaxBeam)
            --print('MaxGround ',MaxGround)
            --print('MaxSky ',MaxSky)
            
            local PVpanelsize = 19.48 -- 12 x 1.64 m x 0.99 m total square Meters
            local PVpanelefficiency = 0.154 -- factory specified PVpanelefficiency
            local MaxPVpanelProduction = PVpanelsize * PVpanelefficiency * MaxBeam *RefractionIndex * tempLossFactor + PVpanelsize * PVpanelefficiency * (MaxTotal-MaxBeam) * tempLossFactor -- RefractionIndex only applied to beam
            --print('clear sky production ', MaxPVpanelProduction)
            --local ActualPVpanelProduction = domoticz.devices(idxPVpanel).actualWatt
            local RatioActualMaxPVpanel=ActualPVpanelProduction / MaxPVpanelProduction
          
            domoticz.devices(idxClarityIndex).updateCustomSensor(domoticz.utils.round(clarityIndex,2))
            domoticz.devices(idxTheoreticalGHI).updateCustomSensor(domoticz.utils.round(theoreticalGHI,0))
            domoticz.devices(idxActualDHI).updateCustomSensor(domoticz.utils.round(actualDiffuseHorizontalIrradianceDHI,0))
            domoticz.devices(idxActualDNI).updateCustomSensor(domoticz.utils.round(actualDirectNormalIrradianceDNI,0))
            domoticz.devices(idxIrradianceBeam).updateCustomSensor(domoticz.utils.round(IrradianceBeam,0))
            domoticz.devices(idxIrradianceGround).updateCustomSensor(domoticz.utils.round(IrradianceGround,0))
            domoticz.devices(idxIrradianceSky).updateCustomSensor(domoticz.utils.round(IrradianceSky,0))
            domoticz.devices(idxIrradianceTotal).updateCustomSensor(domoticz.utils.round(IrradianceTotal,0))
            domoticz.devices(idxTheoreticalProduction).updateCustomSensor(domoticz.utils.round(TheoreticalPVpanelProduction,0))
            domoticz.devices(idxRatioActTheorProduction).updateCustomSensor(domoticz.utils.round(RatioActualTheoreticalPVpanel,2))
            domoticz.devices(idxRatioActMaxProduction).updateCustomSensor(domoticz.utils.round(RatioActualMaxPVpanel,2))
            
            domoticz.log('', domoticz.LOG_INFO)
    		domoticz.log('==================   solar data  ==================', domoticz.LOG_INFO)
    		domoticz.log('Altitude:'..tostring(altitude)..', latitude: ' .. latitude .. ', longitude: ' .. longitude, domoticz.LOG_INFO)
    		domoticz.log('Angular Speed = ' .. angularSpeed .. ' per day', domoticz.LOG_INFO)
    		domoticz.log('Declination = ' .. declination .. '°', domoticz.LOG_INFO)
    		domoticz.log('Universal Coordinated Time (UTC) '.. timeDecimal ..' H.dd', domoticz.LOG_INFO)
    		domoticz.log('Solar Hour '.. solarHour ..' H.dd', domoticz.LOG_INFO)
    		domoticz.log('Altitude of the sun = ' .. sunAltitude .. '°', domoticz.LOG_INFO)
    		domoticz.log('Angular hour = '.. hourAngle .. '°', domoticz.LOG_INFO)
    		domoticz.log('Azimuth of the sun = ' .. sunAzimuth .. '°', domoticz.LOG_INFO)
    		domoticz.log('Duration of the sun stroke of the day = ' .. domoticz.utils.round(sunstrokeDuration,2) ..' H.dd', domoticz.LOG_INFO)
    		domoticz.log('Local relative pressure = ' .. relativePressure .. ' hPa', domoticz.LOG_INFO)
    		domoticz.log('Absolute pressure in atmosphere = ' .. absolutePressure .. ' hPa', domoticz.LOG_INFO)
    		--domoticz.log('Coefficient of mitigation M = ' .. M ..' M0 = '..M0, domoticz.LOG_INFO)
    		domoticz.log('directNormalFactor = ' ..directNormalFactor , domoticz.LOG_INFO)
    		domoticz.log('diffuseFactor = ' ..diffuseFactor , domoticz.LOG_INFO)
    		domoticz.log('directHorizontalFactor = ' ..directHorizontalFactor , domoticz.LOG_INFO)
    		domoticz.log('directPVFactor = ' ..directPVFactor , domoticz.LOG_INFO)
    		domoticz.log('', domoticz.LOG_INFO)
    		
    		domoticz.log('directRadiation = ' ..directRadiation , domoticz.LOG_INFO)
    		domoticz.log('scatteredRadiation = ' ..scatteredRadiation , domoticz.LOG_INFO)
    		domoticz.log('totalRadiation = ' ..totalRadiation , domoticz.LOG_INFO)
    		
	    else
	        domoticz.devices(idxClarityIndex).updateCustomSensor(0)
            domoticz.devices(idxTheoreticalGHI).updateCustomSensor(0)
            domoticz.devices(idxActualDHI).updateCustomSensor(0)
            domoticz.devices(idxActualDNI).updateCustomSensor(0)
            domoticz.devices(idxIrradianceBeam).updateCustomSensor(0)
            domoticz.devices(idxIrradianceGround).updateCustomSensor(0)
            domoticz.devices(idxIrradianceSky).updateCustomSensor(0)
            domoticz.devices(idxIrradianceTotal).updateCustomSensor(0)
            domoticz.devices(idxTheoreticalProduction).updateCustomSensor(0)
            domoticz.devices(idxRatioActTheorProduction).updateCustomSensor(0)
            domoticz.devices(idxRatioActMaxProduction).updateCustomSensor(0)
        end
    end
}
willemd
Posts: 739
Joined: Saturday 21 September 2019 17:55
Target OS: Raspberry Pi / ODroid
Domoticz version: 2024.1
Location: The Netherlands
Contact:

Re: solar panel watchdog

Post by willemd »

You can also have a look at https://forecast.solar.
You can launch a URL with your longitude and lattitude as well as your PV panel size and angle to get a forecast of production.
The feedback can be loaded onto a domoticz device.
Here is my script (with my position and PV data replaced by **)

Code: Select all

return {
	on = {
		timer = {
		    'every hour'
		},
		httpResponses = {
			'solarforecast' -- must match with the callback passed to the openURL command
		}
	},
	logging = {
		level = domoticz.LOG_INFO,
		marker = 'get solar forecast',
	},
	execute = function(domoticz, item)
	    
	    local idxSolarForecast=106  -- device holding the forecast for the next hour

		if (item.isTimer) then
			domoticz.openURL({
				url = 'https://api.forecast.solar/estimate/watthours/period/**lat**/**long**/**decl**/**azi**/**kw**', -- replace with your data, have a look at the API specification at forecast.solar
				method = 'GET',
				callback = 'solarforecast', -- see httpResponses above.
			})
		end

		if (item.isHTTPResponse) then

			if (item.ok) then
				--domoticz.log('item.data ' .. item.data .. '***************************', domoticz.LOG_INFO)
				
				if (item.isJSON) then
				    
				    local messagetype=item.json.message["type"]
				    domoticz.log("message type" .. messagetype, domoticz.LOG_INFO) 
				    
				    if messagetype=="success" then
				        local oneHRahead=os.date("%Y-%m-%d %H:00:00",os.time()+2*60*60)
    				    local twoHRahead=os.date("%Y-%m-%d %H:00:00",os.time()+3*60*60)
    				    local forecastOneHR=tonumber(item.json.result[oneHRahead])
    				    if forecastOneHR==nil then
    				        forecastOneHR=0 
    				    end    
    				    local forecastTwoHR=tonumber(item.json.result[twoHRahead])    
    				    if forecastTwoHR==nil then
    				        forecastTwoHR=0 
    				    end    
    				    domoticz.log("solar forecast for next two hours :" .. forecastOneHR .. " + " .. forecastTwoHR .. " WattHR", domoticz.LOG_INFO) 
    				    domoticz.devices(idxSolarForecast).updateCustomSensor(forecastOneHR)
    				else
    				    domoticz.log("no successfull message", domoticz.LOG_INFO)
    				end    
				else
				    domoticz.log('is not json', domoticz.LOG_INFO) 
				end    
			else
				domoticz.log('There was a problem handling the request', domoticz.LOG_INFO)
				domoticz.log(item, domoticz.LOG_INFO)
			end

		end

	end
}

Post Reply

Who is online

Users browsing this forum: No registered users and 1 guest