Get online airquality data  [SOLVED]

Moderator: leecollings

User avatar
waaren
Posts: 6028
Joined: Tuesday 03 January 2017 14:18
Target OS: Linux
Domoticz version: Beta
Location: Netherlands
Contact:

Re: Get online airquality data

Post by waaren »

mojso wrote: Monday 11 November 2019 16:26 2019-11-11 15:30:00.835 Error: dzVents: Error: (2.5.0) Waqi: An error occurred when calling event handler AirQ4
2019-11-11 15:30:00.835 Error: dzVents: Error: (2.5.0) Waqi: .../pi/domoticz/scripts/dzVents/generated_scripts/AirQ4.lua:136: bad argument #1 to 'for iterator' (table expected, got nil)
Can you insert this line just before line 136 ? It should show the value of type.

Code: Select all

dz.log('type: ' .. tostring(type),dz.LOG_FORCE)
Debian buster, bullseye on RPI-4, Intel NUC.
dz Beta, Z-Wave, RFLink, RFXtrx433e, P1, Youless, Hue, Yeelight, Xiaomi, MQTT
==>> dzVents wiki
mojso
Posts: 86
Joined: Friday 08 November 2019 23:07
Target OS: Raspberry Pi / ODroid
Domoticz version:
Contact:

Re: Get online airquality data

Post by mojso »

Can you insert this line just before line 136 ? It should show the value of type.

Code: Select all

dz.log('type: ' .. tostring(type),dz.LOG_FORCE)

Code: Select all

   dz.log('type: ' .. tostring(type),dz.LOG_FORCE)
i put the command on line 133. but i think i made a mistake.
I think the problem was in the notification. must be set in the sensor and not in the Alert
mojso
Posts: 86
Joined: Friday 08 November 2019 23:07
Target OS: Raspberry Pi / ODroid
Domoticz version:
Contact:

Re: Get online airquality data

Post by mojso »

how to enter this command: if PM10 is greater than 50 and less than 100 changes the light color to yellow

How to make a light-color switch from the value obtained from the device
User avatar
waaren
Posts: 6028
Joined: Tuesday 03 January 2017 14:18
Target OS: Linux
Domoticz version: Beta
Location: Netherlands
Contact:

Re: Get online airquality data

Post by waaren »

mojso wrote: Wednesday 13 November 2019 20:02 how to enter this command: if PM10 is greater than 50 and less than 100 changes the light color to yellow

How to make a light-color switch from the value obtained from the device
What is the name, type , subtype hardware of the colorLight ? (look at the devices tab)
Can you please describe the complete value - color mapping (e.g. 0 - grey, < 50 - Green, < 100 Yellow, etc..)
Debian buster, bullseye on RPI-4, Intel NUC.
dz Beta, Z-Wave, RFLink, RFXtrx433e, P1, Youless, Hue, Yeelight, Xiaomi, MQTT
==>> dzVents wiki
mojso
Posts: 86
Joined: Friday 08 November 2019 23:07
Target OS: Raspberry Pi / ODroid
Domoticz version:
Contact:

Re: Get online airquality data

Post by mojso »

waaren wrote: Wednesday 13 November 2019 21:24
mojso wrote: Wednesday 13 November 2019 20:02 how to enter this command: if PM10 is greater than 50 and less than 100 changes the light color to yellow

How to make a light-color switch from the value obtained from the device
What is the name, type , subtype hardware of the colorLight ? (look at the devices tab)
Can you please describe the complete value - color mapping (e.g. 0 - grey, < 50 - Green, < 100 Yellow, etc..)
name : desk light , type : Arilux AL-LC0x , subtape: rgbwz

0 to 50 green
50 to 100 yellow
101 to 150 orange
151 to 200 Red
201 to 300 purple
301 > Maroon or purple-blinks
User avatar
waaren
Posts: 6028
Joined: Tuesday 03 January 2017 14:18
Target OS: Linux
Domoticz version: Beta
Location: Netherlands
Contact:

Re: Get online airquality data

Post by waaren »

mojso wrote: Wednesday 13 November 2019 21:49 301 > Maroon
Can you try this. (not completely tested yet) and no blinks as I have no clue how to set that.

Code: Select all


--[[
data from http://waqi.info/
An API key is required and can be aquired here ==> http://aqicn.org/data-platform/token/

Based on the original idea and script from @elmortero

http://api.waqi.info/feed/geo:51.852062;4.507676/?token=Your_apikey = Zwartewaalstraat, Rotterdam Zuid
http://api.waqi.info/feed/geo:51.986119;4.934413/?token=Your_apikey = Lopik (co)
http://api.waqi.info/feed/geo:51.867238,4.354981/?token=Your_apikey = Leemkuil (so2)
]]--

return {
    on =    {  
                timer           = { "every 5 minutes" },
                httpResponses   = { "waqi*" }                            -- matches callback wildcard 
            },
        
        data    =   {   
                        safedMessage = { history = true, maxItems = 100 , maxHours = 168 }
                    },
        
        logging =   {   level   =   domoticz.LOG_DEBUG,
                        marker  =   "Waqi" },
    
    execute = function(dz, triggerItem)
        
        local apiKey            = "REMOVED"
        local defaultLocation   = "42.00997;20.97114"
        local sensorType
        local colorDevice = 'desk light' 
        if dz.data.safedMessage.get(1) == nil then
            dz.data.safedMessage.add("Init") 
        end
        
        local geo   = { nearby  = defaultLocation,        -- to get station closest to your home 
                        so2     = "",   -- if value for this item cannot be obtained from "nearby"
                        co      = "",   -- else keep this as an empty string
                        pm25    = "",
                        pm10    = "",
                        o3      = "",
                        no2     = "",
                        aqi     = "",
                      }  
                      
        local sensors = {   so2     = { sensor = 62,  alert = 63 },   -- enter device index Numbers or 0 for sensor and alert "
                            co      = { sensor = 64,  alert = 65    },
                            pm25    = { sensor = 66,  alert = 67  },
                            pm10    = { sensor = 68,  alert = 69  },
                            o3      = { sensor = 70,  alert = 71     },
                            no2     = { sensor = 72,     alert = 73     },
                            aqi     = { sensor = 76,  alert = 75  , aqiSensor = 74},      -- additional Air Quality sensor
                        }
       
        local sensorTypes = { alert = "Alert", aqi = "Air Quality", custom = "Custom Sensor" } 
        
        local function responseType(str)   -- strip waqi_ from string
            return str:gsub("waqi_","")
        end
       
        local function setColor(value, device) 
            local colors =  {
                                green =  {min = 0, max = 50, red = 0, green = 255, blue = 0},
                                yellow = {min = 51, max = 100, red = 255, green = 255, blue = 0},
                                orange = {min = 101, max = 150, red = 255, green = 165, blue = 0},
                                red    = {min = 151, max = 200, red = 255, green = 0, blue = 0},
                                purple = {min = 201, max = 300, red = 128, green = 0, blue = 128},
                                maroon = {min = 301, max = 999999, red = 128, green = 0, blue = 0},
                            }
            for color, control in pairs(colors) do
                if value > control.min and value <= control.max then
                    device.setRGB(control.red,control.green,control.blue)
                end
            end
        end

        local function errorMessage(message,notify)   -- Add entry to log and notify to all subsystems
            dz.log(message,dz.LOG_ERROR)
            if notify then 
                dz.notify(message)
            end
        end    
        
        local function callPollutionURL(location,callback,delay)
            local delay = delay or 1
            local url   = "http://api.waqi.info/feed/geo:".. location .. "/?token=" .. apiKey
            dz.openURL({    url         = url,
                            method      = "GET",
                            callback    = callback
                       }).afterSec(delay)
        end
        
        local function getAirQualityData()
            local delay = 1
            for callType, location in pairs(geo) do
                if location ~= "" then
                    callPollutionURL(location,"waqi_" .. callType,delay) 
                    delay = delay + 30                    
                end    
            end
        end
        
        local function alertLevelAQI(value)
            if value < 50 then return dz.ALERTLEVEL_GREEN,"Excellent" end
            if value < 100 then return dz.ALERTLEVEL_YELLOW,"Poor" end
            if value < 150 then return dz.ALERTLEVEL_ORANGE,"Polluted" end
            return dz.ALERTLEVEL_RED,"Dangerous"
        end
        
        function deviceType(device)
            if device ~= nil then
                if dz.devices(device).deviceType:upper() == "GENERAL" then
                    return dz.devices(device).deviceSubType
                else
                    return dz.devices(device).deviceType
                end
            else
                return nil
            end
        end
        
        
        local function setSensor(sensor,value)
            if sensor ~= 0 and value ~= nil then
               
                if deviceType(sensor) == sensorTypes.custom then 
                    dz.devices(sensor).updateCustomSensor(value)
                elseif deviceType(sensor) == sensorTypes.aqi then
                    dz.devices(sensor).updateAirQuality(value)
                else
                    local alertLevel, alertText = alertLevelAQI(value)
                    local alertString = alertText .. "(" .. tostring(value) .. ")"   
                    if dz.devices(sensor).text ~= alertString then
                        dz.devices(sensor).updateAlertSensor(alertLevel, alertString)
                    end
                end 
            end
        end
        
        local function handleResponse(type)
            local rt = triggerItem.json                        -- rt is just a reference to the data no actual copy is done
            dz.log(triggerItem.data,dz.LOG_DEBUG)
            if triggerItem.json ~= nil and rt.data ~= nil and tonumber(rt.data.aqi) then
                if type == "nearby" then
                    rt.data.iaqi["aqi"] = {}; rt.data.iaqi["aqi"].v = rt.data.aqi    -- handle exception in iaqi as aqi is stored elsewhere
                    
                    for nearbyType, location in pairs(geo) do
                        if nearbyType ~= "nearby" and location == "" then               -- No other location for this type 
                            handleResponse(nearbyType)    
                        end    
                    end
                else
                    for setDevice, idx in pairs(sensors[type]) do
                        dz.log("setDevice" .. setDevice, dz.log_DEBUG)
                        dz.log("idx" .. idx, dz.log_DEBUG)
                        dz.log("sensors[Type]" .. tostring(sensors[Type]), dz.log_DEBUG)
                        dz.log("rt" .. tostring(rt), dz.log_DEBUG)
                        dz.log("rt.data" .. tostring(rt.data), dz.log_DEBUG)
                        dz.log("rt.data.iaqi" .. tostring(rt.data.iaqi), dz.log_DEBUG)
                        dz.log("rt.data.iaqi[type]" .. tostring(rt.data.iaqi[type]), dz.log_DEBUG)
                        setSensor(idx,rt.data.iaqi[type:lower()].v)
                    end
                    dz.log("type" .. tostring(type), dz.log_DEBUG)
                    if type:lower() == 'pm10' then 
                        dz.log("pm10 value" .. tostring(rt.data.iaqi.pm10.v), dz.log_DEBUG)
                        setColor(rt.data.iaqi.pm10.v, colorDevice) 
                    end
                end
            else
                errorMessage("This should not happen")   -- aqi should always be there and set
                if dz.data.safedMessage.get(1).time.secondsAgo > 30 then
                    errorMessage("I will call url again")   -- aqi should always be there and set
                    callPollutionURL(geo[responseType(triggerItem.trigger)],triggerItem.trigger,1)
                    dz.data.safedMessage.add("Extra call to openURL for " .. triggerItem.trigger)
                end    
            end
        end
        
        if triggerItem.isHTTPResponse then
           if triggerItem.ok and triggerItem.isJSON then 
               handleResponse(responseType(triggerItem.trigger))
           else
               errorMessage("Problem with response from waqi",true)  
           end
        else
            getAirQualityData()
        end    
    end
}
Debian buster, bullseye on RPI-4, Intel NUC.
dz Beta, Z-Wave, RFLink, RFXtrx433e, P1, Youless, Hue, Yeelight, Xiaomi, MQTT
==>> dzVents wiki
mojso
Posts: 86
Joined: Friday 08 November 2019 23:07
Target OS: Raspberry Pi / ODroid
Domoticz version:
Contact:

Re: Get online airquality data

Post by mojso »

i get this error

2019-11-14 00:37:02.199 Error: dzVents: Error: (2.5.0) Waqi: An error occurred when calling event handler airqlight
2019-11-14 00:37:02.199 Error: dzVents: Error: (2.5.0) Waqi: ...domoticz/scripts/dzVents/generated_scripts/airqlight.lua:72: attempt to call a nil value (field 'setRGB')


maybe the colors in the Hex/RGB color code will solve this
User avatar
waaren
Posts: 6028
Joined: Tuesday 03 January 2017 14:18
Target OS: Linux
Domoticz version: Beta
Location: Netherlands
Contact:

Re: Get online airquality data

Post by waaren »

mojso wrote: Thursday 14 November 2019 0:43 maybe the colors in the Hex/RGB color code will solve this
No, that will not help. Your device does not support the setRGB method. Can you replace line 72 with these lines

Code: Select all

-- device.setRGB(control.red,control.green,control.blue)
device.dump()
and send me (via PM) the complete resulting log (> 100 lines). It will not change colors but will help me understand why this device does not support the setRGB method yet.
Debian buster, bullseye on RPI-4, Intel NUC.
dz Beta, Z-Wave, RFLink, RFXtrx433e, P1, Youless, Hue, Yeelight, Xiaomi, MQTT
==>> dzVents wiki
User avatar
EdwinK
Posts: 1820
Joined: Sunday 22 January 2017 21:46
Target OS: Raspberry Pi / ODroid
Domoticz version: BETA
Location: Rhoon
Contact:

Re: Get online airquality data

Post by EdwinK »

FireWizard wrote: Saturday 08 June 2019 22:23 Hi,

Do you know that a Domoticz plugin for air quality exists?

See: https://openaq.org
Plugin: https://github.com/Xorfor/Domoticz-OpenAQ-Plugin

It's easier than inventing the wheel twice.
If you are in The Netherlands you can also use the script in this thread:
https://www.domoticz.com/forum/viewtopic.php?t=17334

Regards.
Thanks. Didn't know about the plugin from Xorfor. Makes life much easier. ;) [strike]Do you happen to know how often this plugin updates?[/strike]. Found it, once about every 60 minutes. Which is fine.
Running latest BETA on a Pi-3 | Toon® Thermostat (rooted) | Hue | Tuya | IKEA tradfri | Dashticz V3 on Lenovo Huawei Tablet | Conbee
mojso
Posts: 86
Joined: Friday 08 November 2019 23:07
Target OS: Raspberry Pi / ODroid
Domoticz version:
Contact:

Re: Get online airquality data

Post by mojso »

Hi, I have a question again, the script works great, but I can not see the data from the previous year or it is not saved at all, how can I make it look at the data (report link in other sensors)

Code: Select all

   
 
--[[
data from http://waqi.info/
An API key is required and can be aquired here ==> http://aqicn.org/data-platform/token/

Based on the original idea and script from @elmortero

http://api.waqi.info/feed/geo:51.852062;4.507676/?token=Your_apikey = Zwartewaalstraat, Rotterdam Zuid
http://api.waqi.info/feed/geo:51.986119;4.934413/?token=Your_apikey = Lopik (co)
http://api.waqi.info/feed/geo:51.867238,4.354981/?token=Your_apikey = Leemkuil (so2)
]]--

return {
    on =    {  
                timer           = { "every 30 minutes" },
                httpResponses   = { "waqi*" }                            -- matches callback wildcard 
            },
        
        data    =   {   
                        safedMessage = { history = true, maxItems = 100 , maxHours = 168 }
                    },
        
        logging =   {   level   =   domoticz.LOG_DEBUG,
                        marker  =   "Waqi" },
    
    execute = function(dz, triggerItem)
        
        local apiKey            = "my api"
        local defaultLocation   = "my location"
        local sensorType
        local colorDevice = dz.devices('desk light')
        if dz.data.safedMessage.get(1) == nil then
            dz.data.safedMessage.add("Init") 
        end
        
        local geo   = { nearby  = defaultLocation,        -- to get station closest to your home 
                        so2     = "",   -- if value for this item cannot be obtained from "nearby"
                        co      = "",   -- else keep this as an empty string
                        pm25    = "",
                        pm10    = "",
                        o3      = "",
                        no2     = "",
                        aqi     = "",
                        p       = "", --barometer
                        w       = "",
                       -- wg       = "",
                      }  
                      
        local sensors = {   so2     = { sensor = 62,  alert = 63 },   -- enter device index Numbers or 0 for sensor and alert "
                            co      = { sensor = 64,  alert = 65 },
                            pm25    = { sensor = 66,  alert = 67 },
                            pm10    = { sensor = 68,  alert = 69 },
                            o3      = { sensor = 70,  alert = 71 },
                            no2     = { sensor = 72,     alert = 73 },
                            aqi     = { sensor = 76,  alert = 75  , aqiSensor = 74},      -- additional Air Quality sensor
                            p       = { sensor = 647, alert = 0 }, --barometer
                            w       = { sensor = 656, alert = 0 }, 
                           -- wg       = { sensor = 657, alert = 0 },
                            
                        }
       
        local sensorTypes = { alert = "Alert", aqi = "Air Quality", custom = "Custom Sensor" } 
        
        local function responseType(str)   -- strip waqi_ from string
            return str:gsub("waqi_","")
        end
       
        local function setColor(value, device) 
            local colors =  {
                                green =  {min = 0, max = 50, red = 0, green = 255, blue = 0},
                                yellow = {min = 51, max = 100, red = 255, green = 255, blue = 0},
                                orange = {min = 101, max = 150, red = 255, green = 165, blue = 0},
                                red    = {min = 151, max = 200, red = 255, green = 0, blue = 0},
                                purple = {min = 201, max = 300, red = 128, green = 0, blue = 128},
                                maroon = {min = 301, max = 999999, red = 128, green = 0, blue = 0},
                            }
            for color, control in pairs(colors) do
                if value > control.min and value <= control.max then
                    device.setColor(control.red, control.green, control.blue, device.level)
                    device.dimTo(device.level).afterSec(1)
                    --device.dump()
                end
            end
        end

        local function errorMessage(message,notify)   -- Add entry to log and notify to all subsystems
            dz.log(message,dz.LOG_ERROR)
            if notify then 
                dz.notify(message)
            end
        end    
        
        local function callPollutionURL(location,callback,delay)
            local delay = delay or 1
            local url   = "http://api.waqi.info/feed/geo:".. location .. "/?token=" .. apiKey
            dz.openURL({    url         = url,
                            method      = "GET",
                            callback    = callback
                       }).afterSec(delay)
        end
        
        local function getAirQualityData()
            local delay = 1
            for callType, location in pairs(geo) do
                if location ~= "" then
                    callPollutionURL(location,"waqi_" .. callType,delay) 
                    delay = delay + 30                    
                end    
            end
        end
        
        local function alertLevelAQI(value)
            if value < 50 then return dz.ALERTLEVEL_GREEN,"Excellent" end
            if value < 100 then return dz.ALERTLEVEL_YELLOW,"Poor" end
            if value < 150 then return dz.ALERTLEVEL_ORANGE,"Polluted" end
            return dz.ALERTLEVEL_RED,"Dangerous"
        end
        
        function deviceType(device)
            if device ~= nil then
                if dz.devices(device).deviceType:upper() == "GENERAL" then
                    return dz.devices(device).deviceSubType
                else
                    return dz.devices(device).deviceType
                end
            else
                return nil
            end
        end
        
        
        local function setSensor(sensor,value)
            if sensor ~= 0 and value ~= nil then
               
                if deviceType(sensor) == sensorTypes.custom then 
                    dz.devices(sensor).updateCustomSensor(value)
                elseif deviceType(sensor) == sensorTypes.aqi then
                    dz.devices(sensor).updateAirQuality(value)
                else
                    local alertLevel, alertText = alertLevelAQI(value)
                    local alertString = alertText .. "(" .. tostring(value) .. ")"   
                    if dz.devices(sensor).text ~= alertString then
                        dz.devices(sensor).updateAlertSensor(alertLevel, alertString)
                    end
                end 
            end
        end
        
        local function handleResponse(type)
            local rt = triggerItem.json                        -- rt is just a reference to the data no actual copy is done
            dz.log(triggerItem.data,dz.LOG_DEBUG)
            if triggerItem.json ~= nil and rt.data ~= nil and tonumber(rt.data.aqi) then
                if type == "nearby" then
                    rt.data.iaqi["aqi"] = {}; rt.data.iaqi["aqi"].v = rt.data.aqi    -- handle exception in iaqi as aqi is stored elsewhere
                    
                    for nearbyType, location in pairs(geo) do
                        if nearbyType ~= "nearby" and location == "" then               -- No other location for this type 
                            handleResponse(nearbyType)    
                        end    
                    end
                else
                    for setDevice, idx in pairs(sensors[type]) do
                        dz.log("setDevice" .. setDevice, dz.log_DEBUG)
                        dz.log("idx" .. idx, dz.log_DEBUG)
                        dz.log("sensors[Type]" .. tostring(sensors[Type]), dz.log_DEBUG)
                        dz.log("rt" .. tostring(rt), dz.log_DEBUG)
                        dz.log("rt.data" .. tostring(rt.data), dz.log_DEBUG)
                        dz.log("rt.data.iaqi" .. tostring(rt.data.iaqi), dz.log_DEBUG)
                        dz.log("rt.data.iaqi[type]" .. tostring(rt.data.iaqi[type]), dz.log_DEBUG)
                        setSensor(idx,rt.data.iaqi[type:lower()].v)
                    end
                    dz.log("type" .. tostring(type), dz.log_DEBUG)
                    if type:lower() == 'pm10' then 
                        dz.log("pm10 value" .. tostring(rt.data.iaqi.pm10.v), dz.log_DEBUG)
                        setColor(rt.data.iaqi.pm10.v, colorDevice) 
                    end
                end
            else
                errorMessage("This should not happen")   -- aqi should always be there and set
                if dz.data.safedMessage.get(1).time.secondsAgo > 30 then
                    errorMessage("I will call url again")   -- aqi should always be there and set
                    callPollutionURL(geo[responseType(triggerItem.trigger)],triggerItem.trigger,1)
                    dz.data.safedMessage.add("Extra call to openURL for " .. triggerItem.trigger)
                end    
            end
        end
        
        if triggerItem.isHTTPResponse then
           if triggerItem.ok and triggerItem.isJSON then 
               handleResponse(responseType(triggerItem.trigger))
           else
               errorMessage("Problem with response from waqi",true)  
           end
        else
            getAirQualityData()
        end    
    end
}
  
   
   
      
Attachments
20201206_134017.jpg
20201206_134017.jpg (230.96 KiB) Viewed 3712 times
User avatar
FireWizard
Posts: 1745
Joined: Tuesday 25 December 2018 12:11
Target OS: Raspberry Pi / ODroid
Domoticz version: Beta
Location: Voorthuizen (NL)
Contact:

Re: Get online airquality data

Post by FireWizard »

Hi, @mojso

It is not available. Also many other sensors are not available.

Regards
User avatar
waaren
Posts: 6028
Joined: Tuesday 03 January 2017 14:18
Target OS: Linux
Domoticz version: Beta
Location: Netherlands
Contact:

Re: Get online airquality data

Post by waaren »

mojso wrote: Sunday 06 December 2020 13:35 Hi, I have a question again, the script works great, but I can not see the data from the previous year or it is not saved at all, how can I make it look at the data (report link in other sensors)
That functionality is not implemented for custom sensors. Data is available in database in table multimeter_calendar but if you need a multiyear report you will have to export the data from the database to something like excel.

Code: Select all

cd <domoticz dir>
sudo sqqlite3 domoticz.db
.mode csv
.out report.csv
select * from multimeter_calendar where devicerowid = <device id>;
.quit
Debian buster, bullseye on RPI-4, Intel NUC.
dz Beta, Z-Wave, RFLink, RFXtrx433e, P1, Youless, Hue, Yeelight, Xiaomi, MQTT
==>> dzVents wiki
mojso
Posts: 86
Joined: Friday 08 November 2019 23:07
Target OS: Raspberry Pi / ODroid
Domoticz version:
Contact:

Re: Get online airquality data

Post by mojso »

thanks Waaren for the reply. I do not want to export the data for now, just have a preview in domoticz (Report link) thanks in advance
User avatar
waaren
Posts: 6028
Joined: Tuesday 03 January 2017 14:18
Target OS: Linux
Domoticz version: Beta
Location: Netherlands
Contact:

Re: Get online airquality data

Post by waaren »

mojso wrote: Sunday 06 December 2020 14:24 thanks Waaren for the reply. I do not want to export the data for now, just have a preview in domoticz (Report link) thanks in advance
I don't think it will be implemented for custom sensors any time soon; if at all.
Debian buster, bullseye on RPI-4, Intel NUC.
dz Beta, Z-Wave, RFLink, RFXtrx433e, P1, Youless, Hue, Yeelight, Xiaomi, MQTT
==>> dzVents wiki
mojso
Posts: 86
Joined: Friday 08 November 2019 23:07
Target OS: Raspberry Pi / ODroid
Domoticz version:
Contact:

Re: Get online airquality data

Post by mojso »

waaren wrote: Sunday 06 December 2020 15:07
mojso wrote: Sunday 06 December 2020 14:24 thanks Waaren for the reply. I do not want to export the data for now, just have a preview in domoticz (Report link) thanks in advance
I don't think it will be implemented for custom sensors any time soon; if at all.
thanks for the info
Vomera
Posts: 184
Joined: Wednesday 06 September 2017 9:11
Target OS: Linux
Domoticz version:
Contact:

Re: Get online airquality data

Post by Vomera »

Hi all,

I got some problems with the code. Sometimes it runs the code without an error and the other time i get errors in the logging.
I use only the PM10 status.

Image
dzvents script

Code: Select all

--[[
data from http://waqi.info/
An API key is required and can be aquired here ==> http://aqicn.org/data-platform/token/

Based on the original idea and script from @elmortero


http://api.waqi.info/feed/geo:/?token=TOKEN

]]--

return {
    on =    {  
                timer           = { "every 10 minutes" },
                httpResponses   = { "waqi*" }                            -- matches callback wildcard 
            },
        
        data    =   {   
                        safedMessage = { history = true, maxItems = 100 , maxHours = 168 }
                    },
        
        logging =   {   level   =   domoticz.LOG_DEBUG,
                        marker  =   "Waqi" },
    
    execute = function(dz, triggerItem)
        
        local apiKey            = "apikey"
        local defaultLocation   = "mylocation"
        local sensorType
        
        if dz.data.safedMessage.get(1) == nil then
            dz.data.safedMessage.add("Init") 
        end
        
        local geo   = { nearby  = defaultLocation,        -- to get station closest to your home 
                        SO2     = "",   -- if value for this item cannot be obtained from "nearby"
                        CO      = "",   -- else keep this as an empty string
                        PM25    = "",
                        PM10    = "",
                        O3      = "",
                        NO2     = "",
                        AQI     = "",
                      }  
                      
		local sensors = {  PM10    = { sensor = 13344,  alert = 13345},
                            
                            
                                                    }
       
       
        local sensorTypes = { alert = "Alert", aqi = "Air Quality", custom = "Custom Sensor" } 
        
        local function responseType(str)   -- strip waqi_ from string
            return str:gsub("waqi_","")
        end
       
        local function errorMessage(message,notify)   -- Add entry to log and notify to all subsystems
            dz.log(message,dz.LOG_ERROR)
            if notify then 
                dz.notify(message)
            end
        end    
        
        local function callPollutionURL(location,callback,delay)
            local delay = delay or 1
            local url   = "http://api.waqi.info/feed/geo:".. location .. "/?token=" .. apiKey
            dz.openURL({    url         = url,
                            method      = "GET",
                            callback    = callback
                       }).afterSec(delay)
        end
        
        local function getAirQualityData()
            local delay = 1
            for callType, location in pairs(geo) do
                if location ~= "" then
                    callPollutionURL(location,"waqi_" .. callType,delay) 
                    delay = delay + 30                    
                end    
            end
        end
        
        local function alertLevelAQI(value)
            if value < 50 then return dz.ALERTLEVEL_GREEN,"Excellent" end
            if value < 100 then return dz.ALERTLEVEL_YELLOW,"Poor" end
            if value < 150 then return dz.ALERTLEVEL_ORANGE,"Polluted" end
            return dz.ALERTLEVEL_RED,"Dangerous"
        end
        
        function deviceType(device)
            if device ~= nil then
                if dz.devices(device).deviceType:upper() == "GENERAL" then
                    return dz.devices(device).deviceSubType
                else
                    return dz.devices(device).deviceType
                end
            else
                return nil
            end
        end
        
        
        local function setSensor(sensor,value)
            if sensor ~= 0 and value ~= nil then
               
                if deviceType(sensor) == sensorTypes.custom then 
                    dz.devices(sensor).updateCustomSensor(value)
                elseif deviceType(sensor) == sensorTypes.aqi then
                    dz.devices(sensor).updateAirQuality(value)
                else
                    local alertLevel, alertText = alertLevelAQI(value)
                    local alertString = alertText .. "(" .. tostring(value) .. ")"   
                    if dz.devices(sensor).text ~= alertString then
                        dz.devices(sensor).updateAlertSensor(alertLevel, alertString)
                    end
                end 
            end
        end
        
        local function handleResponse(type)
            local rt = triggerItem.json                        -- rt is just a reference to the data no actual copy is done
            if triggerItem.json ~= nil and rt.data ~= nil and tonumber(rt.data.aqi) then
                if type == "nearby" then
                    rt.data.iaqi["aqi"] = {}; rt.data.iaqi["aqi"].v = rt.data.aqi    -- handle exception in iaqi as aqi is stored elsewhere
                    
                    for nearbyType, location in pairs(geo) do
                        if nearbyType ~= "nearby" and location == "" then               -- No other location for this type 
                            handleResponse(nearbyType)    
                        end    
                    end
            else
                dz.log('type: ' .. tostring(type),dz.LOG_FORCE)
                    for setDevice, idx in pairs(sensors[type]) do
                        setSensor(idx,rt.data.iaqi[type:lower()].v)
                    end
                end
            else
                errorMessage("This should not happen")   -- aqi should always be there and set
                if dz.data.safedMessage.get(1).time.secondsAgo > 30 then
                    errorMessage("I will call url again")   -- aqi should always be there and set
                    callPollutionURL(geo[responseType(triggerItem.trigger)],triggerItem.trigger,1)
                    dz.data.safedMessage.add("Extra call to openURL for " .. triggerItem.trigger)
                end    
            end
        end
        
        if triggerItem.isHTTPResponse then
           if triggerItem.ok and triggerItem.isJSON then 
               handleResponse(responseType(triggerItem.trigger))
           else
               errorMessage("Problem with response from waqi",true)  
           end
        else
            getAirQualityData()
        end    
    end
}
   
 
User avatar
waaren
Posts: 6028
Joined: Tuesday 03 January 2017 14:18
Target OS: Linux
Domoticz version: Beta
Location: Netherlands
Contact:

Re: Get online airquality data

Post by waaren »

Vomera wrote: Friday 05 March 2021 16:14 I got some problems with the code. Sometimes it runs the code without an error and the other time i get errors in the logging.
I use only the PM10 status.
Please try after changing

Code: Select all

                    dz.log('type: ' .. tostring(type),dz.LOG_FORCE)
                    for setDevice, idx in pairs(sensors[type]) do
                        setSensor(idx,rt.data.iaqi[type:lower()].v)
                    end
to

Code: Select all

 
                    if sensors[type] then 
                        for setDevice, idx in pairs(sensors[type]) do
                            setSensor(idx,rt.data.iaqi[type:lower()].v)
                        end
                    else
                        dz.log('No sensor for: ' .. tostring(type),dz.LOG_FORCE)
                    end
Debian buster, bullseye on RPI-4, Intel NUC.
dz Beta, Z-Wave, RFLink, RFXtrx433e, P1, Youless, Hue, Yeelight, Xiaomi, MQTT
==>> dzVents wiki
Vomera
Posts: 184
Joined: Wednesday 06 September 2017 9:11
Target OS: Linux
Domoticz version:
Contact:

Re: Get online airquality data  [SOLVED]

Post by Vomera »

I think the log is better now:

Code: Select all

2021-03-06 11:31:01.826 Status: dzVents: Info: Handling httpResponse-events for: "waqi_nearby"
2021-03-06 11:31:01.826 Status: dzVents: Info: Waqi: ------ Start internal script: Air: HTTPResponse: "waqi_nearby"
2021-03-06 11:31:01.828 Status: dzVents: !Info: Waqi: No sensor for: O3
2021-03-06 11:31:01.836 Status: dzVents: Debug: Waqi: Processing device-adapter for PM10: Alert sensor adapter
2021-03-06 11:31:01.836 Status: dzVents: Debug: Waqi: Processing device-adapter for Lucht: Air quality device
2021-03-06 11:31:01.836 Status: dzVents: !Info: Waqi: No sensor for: SO2
2021-03-06 11:31:01.836 Status: dzVents: !Info: Waqi: No sensor for: AQI
2021-03-06 11:31:01.836 Status: dzVents: !Info: Waqi: No sensor for: CO
2021-03-06 11:31:01.836 Status: dzVents: !Info: Waqi: No sensor for: NO2
2021-03-06 11:31:01.836 Status: dzVents: !Info: Waqi: No sensor for: PM25
2021-03-06 11:31:01.837 Status: dzVents: Info: Waqi: ------ Finished Air
wkossen
Posts: 62
Joined: Friday 06 November 2020 12:12
Target OS: Raspberry Pi / ODroid
Domoticz version:
Contact:

Re: Get online airquality data

Post by wkossen »

Hello,

I'm also experiencing log errors on air quality. in fact, it's flooding the log quite considerably.

2021-10-07 11:00:10.049 Error: dzVents: Error: (3.0.2) An error occurred when calling event handler Air Quality
2021-10-07 11:00:10.049 Error: dzVents: Error: (3.0.2) ...moticz/scripts/dzVents/generated_scripts/Air Quality.lua:51: attempt to index a nil value (field 'pm25')

When i manually enter the url in the browser, i do get information for pm25. maybe the api changed a bit, but i really am not good enough a programmer to troubleshoot this. it used to work, and i don't quite know when it stopped...

Any pointer where i should start searching for clues towards a solution?

thanks
Willem
Jan Jansen
Posts: 229
Joined: Wednesday 30 April 2014 20:27
Target OS: Raspberry Pi / ODroid
Domoticz version: Stable
Location: The Netherlands
Contact:

Re: Get online airquality data

Post by Jan Jansen »

Perhaps the solution via nodered fits you better >> viewtopic.php?p=268329#p268329
Post Reply

Who is online

Users browsing this forum: No registered users and 1 guest