Keresés

Új hozzászólás Aktív témák

  • AcCEsS

    senior tag

    válasz Vodike #3438 üzenetére

    Bocs, csak most volt időm átolvasgatni az új fórumbejegyzéseket (influenza meg ablakcsere van nálunk), de úgy néz ki tényleg működik a Dark Sky időjárás-szolgáltató felhősödési (Octa) infója és a hozzáigazított szkript.

    A szkript egyébként tartalmazza az eredeti Ogimet-féle működési elvet is, a Dark Sky használati lehetőségét csak kiegészítésként adtam hozzá, és a WeatherStation nevű változó O vagy D értékével állítható be, hogy O = Ogimet vagy D = Dark Sky legyen az Octa adat szolgáltatója.

    A működéshez kell egy Dark Sky API kulcsot igényelned a következő módon: A darksky.net oldalon felül válaszd ki a "Developers" módot, alul meg a Try Now gomra nyomva egy gyors regisztráció után megkapod az API kulcsodat, amit be kell másolnod a szkript dsAPIkey változójának aposztrófjai közé. A lekérdezésre az API elég sok infót ad vissza: pl. UV Index, min/max hőmérséklet, nyomás, időjárás előrejelzés stb., a DEBUG módban meg lehet nézni. A szkript jelenleg 10 percenkénti ütemezéssel fut.

    Egyébként a beírásokban a command utáni hülye karakter "json.htm?type=command¶m=udevice&idx=" egy html dekódolási sajátosság, ami lecseréli a "&" utáni "param" nevű tag elejét, én az ilyen hivatkozások paramétertagjainak sorrendjét megfordítottam, és így már jó: "json.htm?param=udevice&type=command&idx="

    Egy ideig még tuti tart nálunk az őrület, ha valaki elakadna segíteni fogok, csak kicsit több lesz a válaszidőm... :)

    És a szkript:

    --[[
    ~/domoticz/scripts/lua/script_time_SolarSensor.lua

    -- Autors ----------------------------------------------------------------
    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 - 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)
    ]]--

    -- Beállítások---------------------------------------------------------------
    -----------------------------------------------------------------------------
    local localhost = '127.0.0.1:8080' -- IP és port
    local city = "VÁROSOD" -- Wunderground API-val használt lakhelyed
    local countryCode = "HU" -- Országkód Wunderground API-hoz
    local idxLux ='szám' -- Lux ID
    local idxSolarAzimuth ='szám' -- Azimut ID
    local idxSolarAltitude ='szám' -- Nap magasság ID
    local idxCloud = 'szám' -- Felhőkitakarás ID
    local idxUserVarOcta='szám' -- Az octa változó ID-je
    local wuAPIkey = "API" -- Weather Underground API kulcsod
    local latitude = LAT -- Otthonod szélességi foka
    local longitude = LON -- Otthonod hosszúsági foka
    local altitude = szám -- Otthonod tengerszint feletti magassága (ha a debug 1-en van, akkor az első lekérdezésnél meg fog jelenni ez a naplóban és beírhatod ide
    local DEBUG = 0 -- 0 nincs, 1 domoticz napló, 2 fájlba írás

    local WeatherStation = 'D' -- octa (felhőkitakarás) információk szolgáltatója: O = Ogimet, D = Dark Sky

    if ( WeatherStation == "O" ) then
    -- Ogimet (https://www.ogimet.com/resynops.phtml.en)
    WMOID = 'szám' -- A hozzád legközelebbi SYNOP állomás kódja
    else
    -- Dark Sky (https://darksky.net)
    dsAPIkey='API' -- Dark Sky API kulcsod
    coordinates=latitude..','..longitude
    end
    -----------------------------------------------------------------------------
    -----------------------------------------------------------------------------

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

    function split(s, delimiter)
    result = {};
    for match in (s..delimiter):gmatch("(.-)"..delimiter) do
    table.insert(result, match);
    end
    return result;
    end

    function round(num, dec)
    if (num == 0 ) then
    return 0
    else
    local mult = 10^(dec or 0)
    return math.floor(num * mult + 0.5) / mult
    end
    end

    function os.capture(cmd, raw)
    local f = assert(io.popen(cmd, 'r'))
    local s = assert(f:read('*a'))
    f:close()
    if raw then return s end
    s = string.gsub(s, '^%s+', '')
    s = string.gsub(s, '%s+$', '')
    s = string.gsub(s, '[\n\r]+', ' ')
    return s
    end
    -----------------------------------------------------------------------------

    commandArray = {}

    time = os.date("*t")
    if ( (time.min % 10) == 0 ) then -- Run every 10 minutes. Check the wundergroud API limitation before changing this

    json = (loadfile "/home/pi/domoticz/scripts/lua/JSON.lua")() -- For Linux
    --json = (loadfile "D:\\Domoticz\\scripts\\lua\\json.lua")() -- For Windows

    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²

    if ( uservariables['octa'] == nil ) then
    print("Error : Did you create the Uservariable octa ?")
    end

    -- API Wunderground
    local config=assert(io.popen('curl http://api.wunderground.com/api/'..wuAPIkey..'/conditions/q/'..countryCode..'/'..city..'.json'))
    local location = config:read('*all')
    config:close()
    local jsonLocation = json:decode(location)

    if ( DEBUG == 1) then
    local latitude = jsonLocation.current_observation.display_location.latitude
    local longitude = jsonLocation.current_observation.display_location.longitude
    local altitude = jsonLocation.current_observation.display_location.elevation
    print('Lat: '..latitude..'Long: '..longitude..'Alt: '..altitude)
    end

    relativePressure = jsonLocation.current_observation.pressure_mb -- if you have an another way to get the Pressure, (local barometer ...) then you may optimize the script and avoid the call to api.wunderground)
    -----------------------------------------------------------------------------

    local year = os.date("%Y")
    local numOfDay = os.date("%j")

    if ( leapYear(year) == true ) then
    nbDaysInYear = 366 -- How many days in the year ?
    else
    nbDaysInYear = 365
    end

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

    local azimuth = math.acos((math.sin(math.rad(Declinaison)) - 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(Declinaison)) * math.sin(math.rad(hourlyAngle))) / math.cos(math.rad(sunAltitude))

    if (sinAzimuth<0) then
    azimuth=360-azimuth
    end

    sunstrokeDuration = math.deg(2/15 * math.acos(- math.tan(math.rad(latitude)) * math.tan(math.rad(Declinaison)))) -- duration of sunstroke in the day . Not used in this calculation.
    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
    absolutePressure = relativePressure - round((altitude/ 8.3),1) -- hPa
    sinusSunAltitude = math.sin(math.rad(sunAltitude))
    M0 = math.sqrt(1229 + math.pow(614 * sinusSunAltitude,2)) - 614 * sinusSunAltitude
    M = M0 * relativePressure/absolutePressure

    if (DEBUG == 1) then
    print('<b style="color:Blue"============== SUN LOG ==================</b>')
    print(os.date("%Y-%m-%d %H:%M:%S", os.time()))
    print(city .. ", latitude:" .. latitude .. ", longitude:" .. longitude)
    print("Home altitude = " .. tostring(altitude) .. " m")
    print("number Of Day = " .. numOfDay)
    if ( nbDaysInYear == 366 ) then
    print(year .." is a leap year !")
    else
    print(year.." is not a leap year")
    end
    print("Angular Speed = " .. angularSpeed .. " per day")
    print("Declinaison = " .. Declinaison .. "°")
    print("Universel coordinatesinated Time (UTC)".. timeDecimal .." H.dd")
    print("Solar Hour ".. solarHour .." H.dd")
    print("Altitude of the sun = " .. sunAltitude .. "°")
    print("Angular hourly = ".. hourlyAngle .. "°")
    print("Azimuth of the sun = " .. azimuth .. "°")
    print("Duration of the sunstroke of the day = " .. round(sunstrokeDuration,2) .." H.dd") -- not used
    print("Radiation max in atmosphere = " .. round(RadiationAtm,2) .. " W/m²")
    print("Local relative pressure = " .. relativePressure .. " hPa")
    print("Absolute pressure in atmosphere = " .. absolutePressure .. " hPa")
    print("Coefficient of mitigation M = " .. M .." M0:"..M0)
    end
    -----------------------------------------------------------------------------

    if ( WeatherStation == "O" ) then
    -------- Ogimet --------
    -- Get SYNOP message from Ogimet web site
    hourUTCminus1 = os.date("!%H")-1

    if ( string.len(hourUTCminus1) == 1 ) then
    hourUTCminus1 = "0" .. hourUTCminus1
    end

    UTC = os.date("%Y%m%d").. hourUTCminus1.."00" -- os.date("!%M")

    if ( DEBUG == 1 ) then
    local WMOID = jsonLocation.current_observation.display_location.wmo
    end

    cmd='curl "http://www.ogimet.com/cgi-bin/getsynop?block='..WMOID..'&begin='..UTC..'"'

    if ( DEBUG == 1) then
    print(cmd)
    end

    local ogimet=assert(io.popen(cmd))
    local synop = ogimet:read('*all')
    ogimet:close()

    if ( DEBUG == 1) then
    print('ogimet:'..synop)
    end

    if ( string.find(synop,"Status: 500") == nil ) then
    rslt = split(synop,",")
    CodeStation = rslt[1]
    rslt = split(synop, " "..CodeStation.. " ")
    Trame = string.gsub(rslt[2], "=", "")
    Trame = CodeStation .." ".. Trame
    rslt = split(Trame, " ")
    Octa = string.sub(rslt[3], 1, 1) -- 3rd char is the cloud layer. 0=no cloud, 1-8=cloudy from 1 to 8 max, 9=Fog, /=no data
    if ( Octa == "/" ) then -- not defined ? take the previous value
    Octa = uservariables['octa']
    elseif ( Octa == "9" ) then
    Octa = 8
    end
    else
    Octa = uservariables['octa']
    end
    else
    -------- Dark Sky --------
    -- Get weather information from Dark Sky web site
    cmd='curl -s "https://api.darksky.net/forecast/'..dsAPIkey..'/'..coordinates..'?lang=hu&units=ca&exclude=hourly,flags"'

    if ( DEBUG == 1) then
    print(cmd)
    end

    result=os.capture(cmd)

    -- Reading values from json
    local jsonValeur = json:decode(result)

    Octa = jsonValeur.currently.cloudCover*100

    if ( Octa < 0 or Octa > 100 ) then -- Ha valami hibás infó jönne, az előző tárolt adattal számol
    Octa = uservariables['octa']
    end

    if ( DEBUG == 1) then
    val_Patm = jsonValeur.currently.pressure
    val_UV = jsonValeur.currently.uvIndex
    -- val_Vis = jsonValeur.currently.visibility -- Ezt az infót jelenleg nem adja vissza a json kérés
    val_Cloud = jsonValeur.currently.cloudCover*100
    val_Tmin = round(jsonValeur.daily.data[1].temperatureLow,1)
    val_Tmax = round(jsonValeur.daily.data[1].temperatureHigh,1)
    icon = jsonValeur.daily.data[1].icon
    summary = jsonValeur.daily.data[1].summary

    -- Converting Dark Sky icon into domoticz forecast code
    if icon=="clear-day" or icon=="clear-night" then prev=1 --Sunny
    elseif icon=="rain" or icon=="snow" or rain=="sleet" then prev=6 --Cloudy/Rain
    elseif icon=="wind" or icon=="fog" then prev=0 --Stable
    elseif icon=="cloudy" then prev=2 --Cloudy
    elseif icon=="partly-cloudy-day" or icon=="partly-cloudy-night" then prev=3 --Unstable
    else prev=5 --Unknown
    end

    forecast=summary.." Min : "..val_Tmin.."°C Max : "..val_Tmax.."°C"

    print("Pressure : "..val_Patm)
    print("UV Index : "..val_UV)
    -- print("Visibility : "..val_Vis) -- Ezt az infót jelenleg nem adja vissza a json kérés
    print("Temperature min J+1 : "..val_Tmin)
    print("Temperature max J+1 : "..val_Tmax)
    print("Icon : "..icon.." "..prev)
    print("Summary : "..summary)
    print("Previsions : "..forecast)
    end
    end
    -----------------------------------------------------------------------------

    commandArray[#commandArray + 1] = {['Variable:octa'] = tostring(Octa)}

    if ( WeatherStation == "O" ) then -- Factor of mitigation for the cloud layer
    -------- Ogimet --------
    Kc=1-0.75*math.pow(Octa/8,3.4)
    else
    -------- Dark Sky --------
    Kc=1-0.75*math.pow(Octa/100,3.4) -- Remélem ez így jó
    end

    if ( sunAltitude > 1 ) then -- Below 1° of Altitude , the formulae reach their limit of precision.
    directRadiation = RadiationAtm * math.pow(0.6,M) * sinusSunAltitude
    scatteredRadiation = RadiationAtm * (0.271 - 0.294 * math.pow(0.6,M)) * sinusSunAltitude
    totalRadiation = scatteredRadiation + directRadiation
    Lux = totalRadiation / 0.0079 -- 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 / 0.0079 -- 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

    if ( DEBUG == 1 ) then
    if ( WeatherStation == "O" ) then
    -------- Ogimet --------
    print("Ogimet")
    print("Station SYNOP = " .. WMOID)
    else
    print("Dark Sky")
    end
    print( Octa .. " Octa")
    print("Kc = " .. Kc)
    print("Direct Radiation = ".. round(directRadiation,2) .." W/m²")
    print("Scattered Radiation = ".. round(scatteredRadiation,2) .." W/m²")
    print("Total radiation = " .. round(totalRadiation,2) .." W/m²")
    print("Total Radiation in lux = ".. round(Lux,2).." Lux")
    print("and at last, Total weighted lux = ".. round(weightedLux,2).." Lux")
    end
    -----------------------------------------------------------------------------

    if ( tonumber(otherdevices_svalues['Lux'])+round(weightedLux,0) > 0 ) then -- 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'])
    commandArray[#commandArray + 1]={['OpenURL']="http://"..localhost.."/json.htm?param=udevice&type=command&idx="..idxLux.."&nvalue=0&svalue="..tostring(round(weightedLux,0)) }
    end
    commandArray[#commandArray + 1]={['OpenURL']="http://"..localhost.."/json.htm?param=udevice&type=command&idx="..idxSolarAzimuth.."&nvalue=0&svalue="..tostring(round(azimuth,0)) }
    commandArray[#commandArray + 1]={['OpenURL']="http://"..localhost.."/json.htm?param=udevice&type=command&idx="..idxSolarAltitude.."&nvalue=0&svalue="..tostring(round(sunAltitude,0)) }

    if ( WeatherStation == "O" ) then
    -------- Ogimet --------
    commandArray[#commandArray + 1]={['OpenURL']="http://"..localhost.."/json.htm?param=udevice&type=command&idx="..idxCloud.."&nvalue=0&svalue="..tostring(round(Octa*100/8,0)) }
    else
    -------- Dark Sky --------
    commandArray[#commandArray + 1]={['OpenURL']="http://"..localhost.."/json.htm?param=udevice&type=command&idx="..idxCloud.."&nvalue=0&svalue="..tostring(round(Octa,0)) }
    end

    -----------------------------------------------------------------------------

    if ( DEBUG == 2 ) then
    logDEBUG=os.date("%Y-%m-%d %H:%M:%S",os.time())
    logDEBUG=logDEBUG.." Azimuth:" .. azimuth .. " Height:" .. sunAltitude
    logDEBUG=logDEBUG.." Octa:" .. Octa.." KC:".. Kc
    logDEBUG=logDEBUG.." Direct:"..directRadiation.." inDirect:"..scatteredRadiation.." TotalRadiation:"..totalRadiation.." LuxCloud:".. round(weightedLux,2)
    os.execute('echo '..logDEBUG..' >>logSun.txt') -- compatible Linux & Windows
    end
    end

    return commandArray

    AcCEsS (https://www.metalenciklopedia.hu)

Új hozzászólás Aktív témák