Netatmo

Forum dédié aux problématiques concernant les scripts pour DomoticZ.
Entourez votre code et les logs avec les balises nommées code grâce au bouton <\>.
didier3164
Messages : 15
Inscription : 02 nov. 2018, 11:28

Netatmo

Message par didier3164 »

Bonjour,
Je propose ce petit script à ceux qui possèdent un équipement Netatmo et qui voudraient récupérer les données des stations météo Netatmo.
Tout d'abord, il faut s’inscrire sur leur site pour créer une application.
On récupère alors les identifiants nécessaires à la connexion:
  • L'identifiant du client: client_id
  • La clé secrète: client_secret
Le scope dépend de ce que vous voulez faire et se comprends par lui même.

Code : Tout sélectionner

--[[
Developpé par Didier3164, version du 20/05/2020
--]]
------------------------------------------------------------------------
-- ELEMENTS D'AUTHENTIFICATION
------------------------------------------------------------------------
local client_id = 'xxxxxxxxxxxxxxxxxxxxxx'
local client_secret = 'xxxxxxxxxxxxxxxxxxxxxx'
local username = 'xxxxxxxxxx@xxxxxx.com'
local password = '################'
local scope = 'read_thermostat write_thermostat read_station read_camera'
------------------------------------------------------------------------
--[[
Les données d'authentification sont stoquées dans des variables partagées accessibles par d'autre scripts
dz.globalData.refresh_token = refresh_token
dz.globalData.access_token = access_token
--]]

return {
	active = true,
	logging = {
					-- level	 =   domoticz.LOG_DEBUG, -- Seulement un niveau peut être actif; commenter les autres
					-- level	=   domoticz.LOG_INFO, 
					-- level	=   domoticz.LOG_ERROR,											
					-- level	=   domoticz.LOG_DEBUG,
					-- level	=   domoticz.LOG_MODULE_EXEC_INFO,
		marker = 'Netatmo Token'
	},
	on = {
		httpResponses = {'Netatmo_getToken'},
		system = {'start'}
	},
	execute = function(dz, item)
        if item.isSystemEvent then
			-- Initialisation du token
			dz.openURL({
                url = 'https://api.netatmo.com/oauth2/token',
                method = 'POST',
                callback = 'Netatmo_getToken',
                headers = { ['Content-Type']='application/x-www-form-urlencoded' },
                postData = 'grant_type=password&username='..username
                    ..'&password='..password
                    ..'&client_id='..client_id
                    ..'&client_secret='..client_secret
                    ..'&scope='..scope
             })
		elseif item.isHTTPResponse then
			if item.ok then
				if item.isJSON then
				    if item.callback == 'Netatmo_getToken' then
                        dz.globalData.refresh_token = item.json.refresh_token
                        dz.globalData.access_token = item.json.access_token
                        local Time = require('Time')
                        local currentTime = Time()
                        local expireTime = dz.time.addSeconds(item.json.expires_in)
                        -- Refresh du token
            			dz.openURL({
                            url = 'https://api.netatmo.com/oauth2/token',
                            method = 'POST',
                            callback = 'Netatmo_getToken',
                            headers = { ['Content-Type']='application/x-www-form-urlencoded' },
                            postData = 'grant_type=refresh_token&refresh_token='..dz.globalData.refresh_token
                                ..'&client_id='..client_id
                                ..'&client_secret='..client_secret
                         }).afterSec(item.json.expire_in)
                    end
                end
            end
        end
	end
}
Le script ci-après va lire les données des stations météo Netatmo comprises dans un carré définit par les coordonnées
de ses angles opposés en latitude et longitude.

Code : Tout sélectionner

--[[
Developpé par Didier3164, version du 01/05/2020
La latitude et la longitude doivent avoir été saisies dans les paramètres de Domoticz et les dispositifs créés
--]]
local meteo_idx = 1	-- Température Humidité Pression
local vent_idx = 2	-- Vent
local pluie_idx = 3	-- précipitation

return {
    active = true,
	logging = {
					-- level	 =   domoticz.LOG_DEBUG, -- Seulement un niveau peut être actif; commenter les autres
					-- level	=   domoticz.LOG_INFO, 
					-- level	=   domoticz.LOG_ERROR,											
					-- level	=   domoticz.LOG_DEBUG,
					-- level	=   domoticz.LOG_MODULE_EXEC_INFO,
		marker = 'Météo Netatmo'
	},
	on = {
		timer = { 'every 10 minutes' },	
		httpResponses = { 'meteoNetatmo' }
	},
	data = {
	    dist = { initial = 0 }
	    },
	execute = function(dz, item)
	    if (item.isTimer) then
	        dz.data.dist = 0
	    	-- Lecture des températures extérieures
			-- lat_ne, lon_ne, lat_sw, lon_sw définissent les coordonneés (ne: Nord-Est, sw: Sud-Ouest) du carré englobant les stations à lire
			
			dz.openURL({url = 'https://api.netatmo.com/api/getpublicdata',
				method = 'POST',
				callback = 'meteoNetatmo',
				postData = {
					access_token = dz.globalData.access_token,
					lat_ne = 43.673631,
					lon_ne = 1.39071,
					lat_sw = 43.625647,
					lon_sw = 1.36653,
					filter = true
				}
			 })
        end
		if (item.isHTTPResponse) then
			if (item.ok) then
				if (item.isJSON) then
					if (item.json.body.errors == nil) then
						if (item.callback == 'meteoNetatmo') then
                            local mesures = {}
                            local nbr_mesures = {}
                            for stationId,station in pairs(item.json.body) do
                                for measureId,measure in pairs(station.measures) do
                                    if measure.type ~= nil then
                                        for resId,res in pairs(measure.res) do
                                            for i,valeur in pairs(res) do
                                                if mesures[measure.type[i]] == nil then
                                                    mesures[measure.type[i]] = 0 
                                                    nbr_mesures[measure.type[i]] = 0
                                                end
                                                mesures[measure.type[i]] = mesures[measure.type[i]] + valeur
                                                nbr_mesures[measure.type[i]] = nbr_mesures[measure.type[i]] + 1
                                            end
                                        end
                                    end
                                    if measure.rain_timeutc ~= nil then
                                         dz.log('Données pluie: '..measure.rain_live, dz.LOG_INFO)
                                        --rain_60min, rain_24h, rain_live
                                        if mesures['rain_60min'] == nil then
                                            --Initialisation du tableau
                                            mesures['rain_60min'] = 0
                                            nbr_mesures['rain_60min'] = 0
                                            mesures['rain_24h'] = 0
                                            nbr_mesures['rain_24h'] = 0
                                            mesures['rain_live'] = 0 
                                            nbr_mesures['rain_live'] = 0 
                                        end
                                        if measure.rain_60min >= 0 then 
                                            mesures['rain_60min'] = mesures['rain_60min'] + measure.rain_60min
                                            nbr_mesures['rain_60min'] = nbr_mesures['rain_60min'] + 1
                                        end
                                        if measure.rain_24h == nil then measure.rain_24h = 0 end
                                        if measure.rain_24h >= 0 then
                                            mesures['rain_24h'] = mesures['rain_24h'] + measure.rain_24h
                                            nbr_mesures['rain_24h'] = nbr_mesures['rain_24h'] + 1
                                        end
                                        if measure.rain_live == nil then measure.rain_live = 0 end
                                        if measure.rain_live >= 0 then
                                            mesures['rain_live'] = mesures['rain_live'] + measure.rain_live
                                            nbr_mesures['rain_live'] = nbr_mesures['rain_live'] + 1
                                        end
                                    end
                                    if measure.wind_timeutc ~= nil then
                                        -- Latitude: station.place.location[1]
                                        -- Longitude: station.place.location[2]
                                        x = (station.place.location[2] - dz.settings.location.longitude) * math.cos((station.place.location[1] + dz.settings.location.latitude)/2)
                                        y = station.place.location[1] - dz.settings.location.latitude
                                        distance = 1.852 * 60 * math.sqrt(x^2 + y^2)
                                        if dz.data.dist == 0 then dz.data.dist = distance end
                                        if distance <= dz.data.dist then
											-- Station la plus proche
                                            dz.data.dist = distance
                                            mesures['wind_strength'] = measure.wind_strength
                                            
                                            mesures['wind_angle'] =  measure.wind_angle
                                            
                                            mesures['gust_strength'] =  measure.gust_strength
                                            
                                            mesures['gust_angle'] = measure.gust_angle
                                        end
                                    end
                                end
                            end
							local meteoPluie = 0
                            if nbr_mesures['rain_live'] ~= nil then
                                dz.globalData.meteoPluie = mesures['rain_live'] / nbr_mesures['rain_live']
                                local rate = mesures['rain_60min'] / nbr_mesures['rain_60min']
                                dz.devices(pluie_idx).updateRain(rate, meteoPluie)
                            else
                                dz.log('Aucune donnée de pluie.', dz.LOG_INFO)
                            end
							local meteoTemp = 0
                            if nbr_mesures['temperature'] ~= nil then
                                meteoTemp = mesures['temperature'] / nbr_mesures['temperature']
                            else
                                dz.log('Aucune donnée de température.', dz.LOG_INFO)
                            end
							local meteoPression = 0
                            if nbr_mesures['pressure'] ~= nil then
                                meteoPression = mesures['pressure'] / nbr_mesures['pressure']
                            else
                                dz.log('Aucune donnée de pression.', dz.LOG_INFO)
                            end
							local meteoHumid = 0							
                            if nbr_mesures['humidity'] ~= nil then
                                meteoHumid = mesures['humidity'] / nbr_mesures['humidity']
                            else
                                dz.log("Aucune donnée d'humidite.", dz.LOG_INFO)
                            end
                            if mesures['wind_strength'] ~= nil then
                                local speed = mesures['wind_strength']
                                local bearing = mesures['wind_angle']
                                local gust = mesures['gust_strength']
								local Sector = {"N","NNE","NE","ENE","E","ESE","SE","SSE","S","SSW","SW","WSW","W","WNW","NW","NNW","N"}
                                local direction = Sector[dz.utils.round((bearing%360)/ 22.5,0)+1]
                                dz.devices(vent_idx).updateWind(dz.utils.round(bearing,0), direction, speed, gust, dz.utils.round(meteoTemp,1), chill)
                            end
						    dz.devices(meteo_idx).updateTempHumBaro(dz.utils.round(meteoTemp,1), dz.utils.round(meteoHumid,1),0, dz.utils.round(meteoPression,0), dz.BARO_NOINFO)
                        end
                    end
                end
            end
        end
	end
}
tekkharibo
Messages : 282
Inscription : 28 déc. 2017, 09:51
Localisation : Albi

Re: Netatmo

Message par tekkharibo »

Salut, je ne comprends pas très bien le fonctionnement de ce script, c'est pour récupérer les données des stations netatmo de d'autres personnes autour de nous ?

Que fait-on de ses données ? Est ce que ça réalise une moyenne du carré ?

Merci

Envoyé de mon Redmi Note 7 en utilisant Tapatalk

Projet de domotique low-cost : Mon installation MAJ 01/05/2020
Dans le doute reboot !
Si vous êtes pas loin d'Albi on peut se rencontrer pour partager
copernicnic
Messages : 11
Inscription : 03 déc. 2019, 15:28

Re: Netatmo

Message par copernicnic »

Hello, merci pour le script.
je souhaiterais l'integrer à mon domoticz mais je galère pour trouvé ou est comment l'installer.
serait il possible de m'éclairer ?
didier3164
Messages : 15
Inscription : 02 nov. 2018, 11:28

Re: Netatmo

Message par didier3164 »

Bonsoir,
Je me rends compte que le script n'estpas trés propre. Désolé :(
Il s'agit des stations météo achetés par d'autres et ayant rendus leurs données publiques.
Le script est a créer dans le Menu Configuration >> Plus d'options >> Evenements
Tu clique sur le + puis DzVents puis minimal
Tu remplaces le contenu par celui-là.

Le script fait 2 choses:
- La moyenne des stations météo comprises dans la zone définie par ses coordonnées (les miennes dans le script)
- Lecture de la vitesse du vent de la station la plus proche (à vol d'oiseau)

J'ai depuis acheté ma propre station et développé le script ci-dessous:

Code : Tout sélectionner

local macAdress = 'xx:xx:xx:xx:xx:xx'   -- Adresse MAC de ma station météo
-- Capteurs virtuels à créer --
local modExtIdx = xxx-- Index module extérieur (Température/Humidité/Baromètre)
local nivSonIdx = xxx -- Index Niveau sonore du module intérieur (Sound)
local pilesModExtIdx = xxx -- Index niveau batterie du module extérieur (Custom sensor)
local qualiteIdx = xxx -- Index Qualité de l'air du module intérieur (Qualité de l'air)
local modIntIdx = xxx -- Index module intérieur (Température/Humidité)

-- https://dev.netatmo.com/apidocumentation/weather#getstationsdata

return {
    active = true,
	logging = {
					-- level	 =   domoticz.LOG_DEBUG, -- Seulement un niveau peut être actif; commenter les autres
					-- level	=   domoticz.LOG_INFO,
					-- level	=   domoticz.LOG_ERROR,
					-- level	=   domoticz.LOG_DEBUG,
					-- level	=   domoticz.LOG_MODULE_EXEC_INFO,
		marker = 'Netatmo maMétéo V1.0'
	},
	on = {
		timer = { 'every 1 minutes' },	
		httpResponses = { 'maMeteoNetatmo' }
	},
	data = {
	    dist = { initial = 0 }
	    },
	execute = function(dz, item)
	    if (item.isTimer) then
	        dz.data.dist = 0
	    	-- Lecture des températures extérieures
			dz.openURL({url = 'https://api.netatmo.com/api/getstationsdata',
				method = 'POST',
				callback = 'maMeteoNetatmo',
				postData = {
					access_token = dz.globalData.access_token,
					device_id = macAdress ,
					get_favorites = false
				}
			 })
        end
		if (item.isHTTPResponse) then
			if (item.ok) then
				if (item.isJSON) then
					if (item.json.body.errors == nil) then
						if (item.callback == 'maMeteoNetatmo') then
                            local mesures = {}
                            local nbr_mesures = {}
                            for equipId,equipement in pairs(item.json.body.devices) do
                                --dz.utils.dumpTable(equipement)
                                dz.log('Module: '..equipement.station_name,dz.LOG_INFO)
                                local joignable = 'Non'
                                if equipement.reachable then joignable = 'Oui' end
                                dz.log('     - Joignable: '..joignable,dz.LOG_INFO)
                                dz.log('     - Signal: '..equipement.wifi_status,dz.LOG_INFO)   --&rssi=10&battery=89
                                dz.log('     - Date: '..dz.time.timestampToDate(equipement.dashboard_data.time_utc,'date time'),dz.LOG_INFO)
                                dz.log('     - Temperature: '..equipement.dashboard_data.Temperature,dz.LOG_INFO)
                                dz.log('     - Humidité: '..equipement.dashboard_data.Humidity..' %',dz.LOG_INFO)
                                
                                local wifiLevel = dz.utils.round((100-equipement.wifi_status) /10,1)
                                dz.openURL({url = dz.settings['url']..'/json.htm?type=command&param=udevice&idx='..modIntIdx
                                    ..'&nvalue=0&svalue='..dz.utils.round(equipement.dashboard_data.Temperature,1)..';'
                                    ..dz.utils.round(equipement.dashboard_data.Humidity,1)
                                    ..';0&rssi='..wifiLevel} )
                                
                                dz.log('     - CO2: '..equipement.dashboard_data.CO2..' ppm',dz.LOG_INFO)
                                dz.devices(qualiteIdx).updateAirQuality(equipement.dashboard_data.CO2)
                                
                                dz.log('     - Bruit: '..equipement.dashboard_data.Noise..' db',dz.LOG_INFO)
                                dz.devices(nivSonIdx).updateSoundLevel(equipement.dashboard_data.Noise)
                                
                                dz.log('     - Pression: '..equipement.dashboard_data.Pressure..' mbar',dz.LOG_INFO)
                                
                                dz.log('     - Pression absolue: '..equipement.dashboard_data.AbsolutePressure..' mbar',dz.LOG_INFO)
                                dz.log('=========================================================================',dz.LOG_INFO)

                                for moduleId,module in pairs(equipement.modules) do
                                    --dz.utils.dumpTable(module)
                                    dz.log('Module: '..module.module_name,dz.LOG_INFO)
                                    local joignable = 'Non'
                                    if module.reachable then joignable = 'Oui' end
                                    dz.log('     - Joignable: '..joignable,dz.LOG_INFO)
                                    dz.log('     - Signal: '..module.rf_status,dz.LOG_INFO)
                                    dz.log('     - Date de la mesure: '..dz.time.timestampToDate(module.dashboard_data.time_utc,'date time'),dz.LOG_INFO)
                                    
                                    dz.log('     - Temperature: '..module.dashboard_data.Temperature,dz.LOG_INFO)
                                    dz.globalData.meteo.temp = module.dashboard_data.Temperature
                                    
                                    dz.log('     - Humidité: '..module.dashboard_data.Humidity..' %',dz.LOG_INFO)
                                    
                                    dz.log('     - Piles: '..module.battery_vp..' v ('..module.battery_percent..'%)',dz.LOG_INFO)
                                    local titre = 'Module extérieur ('
                                    if module.battery_vp <= 4000 then
                                        titre = titre..'Trés bas'
                                    elseif module.battery_vp <= 4500 then
                                        titre = titre..'Moyen'
                                    elseif module.battery_vp <= 5000 then
                                        titre = titre..'Haut'
                                    else
                                        titre = titre..'Pleine'
                                    end
                                    titre = titre..'/'..module.battery_percent..'%)'
                                    dz.devices(pilesModExtIdx).rename(titre)
                                    local niveau = module.battery_vp/2000
                                    dz.devices(pilesModExtIdx).updateCustomSensor(niveau)
                                    
                                    local rfLevel = dz.utils.round((100-module.rf_status) /10,1)
                                    dz.openURL({url = dz.settings['url']..'/json.htm?type=command&param=udevice&idx='..modExtIdx
                                        ..'&nvalue=0&svalue='..dz.utils.round(dz.globalData.meteo.temp,1)..';'
                                        ..dz.utils.round(dz.globalData.meteo.humid,1)
                                        ..';0;'..dz.utils.round(dz.globalData.meteo.pression,0)..';0&rssi='..rfLevel..'&battery='..module.battery_percent} )
                                    
                                end
                            end
                        end
                    end
                end
            else
                dz.utils.dumpTable(item)
                dz.log('Erreur ma météo Netatmo ('..item.callback..'): '..item.statusCode..' - '..item.statusText,dz.LOG_ERROR)
            end
        end
        
	end
}
Répondre