APIs e Microsserviços

24 jul, 2018

Brincando com Grafana e APIs de clima

Publicidade

Hoje eu quero brincar com o Grafana. Deixe-me mostrar minha ideia: eu tenho um sensor de temperatura Beewi – eu brinquei com ele anteriormente.

Hoje eu quero mostrar a temperatura dentro de um painel do Grafana e também pretendo brincar com a API openweathermap. Primeiro, eu quero recuperar a temperatura do dispositivo Beewi. Eu tenho um script de nó que se conecta via Bluetooth ao dispositivo usando uma biblioteca nobre.

Eu só preciso passar o endereço mac do sensor e obtenho um JSON com a temperatura atual.

#!/usr/bin/env node
noble = require('noble');
 
var status = false;
var address = process.argv[2];
 
if (!address) {
    console.log('Usage "./reader.py <sensor mac address>"');
    process.exit();
}
 
function hexToInt(hex) {
    var num, maxVal;
    if (hex.length % 2 !== 0) {
        hex = "0" + hex;
    }
    num = parseInt(hex, 16);
    maxVal = Math.pow(2, hex.length / 2 * 8);
    if (num > maxVal / 2 - 1) {
        num = num - maxVal;
    }
 
    return num;
}
 
noble.on('stateChange', function(state) {
    status = (state === 'poweredOn');
});
 
noble.on('discover', function(peripheral) {
    if (peripheral.address == address) {
        var data = peripheral.advertisement.manufacturerData.toString('hex');
        out = {
            temperature: parseFloat(hexToInt(data.substr(10, 2)+data.substr(8, 2))/10).toFixed(1)
        };
        console.log(JSON.stringify(out))
        noble.stopScanning();
        process.exit();
    }
});
 
noble.on('scanStop', function() {
    noble.stopScanning();
});
 
setTimeout(function() {
    noble.stopScanning();
    noble.startScanning();
}, 2000);
 
 
setTimeout(function() {
    noble.stopScanning();
    process.exit()
}, 20000);

E finalmente outro script (desta vez, um script Python) para coletar dados da API openweathermap, coletar dados do script de nó e armazenar as informações em um banco de dados influxdb.

from sense_hat import SenseHat
from influxdb import InfluxDBClient
import datetime
import logging
import requests
import json
from subprocess import check_output
import os
import sys
from dotenv import load_dotenv
 
logging.basicConfig(level=logging.INFO)
 
current_dir = os.path.dirname(os.path.abspath(__file__))
load_dotenv(dotenv_path="{}/.env".format(current_dir))
 
sensor_mac_address = os.getenv("BEEWI_SENSOR")
openweathermap_api_key = os.getenv("OPENWEATHERMAP_API_KEY")
influxdb_host = os.getenv("INFLUXDB_HOST")
influxdb_port = os.getenv("INFLUXDB_PORT")
influxdb_database = os.getenv("INFLUXDB_DATABASE")
 
reader = '{}/reader.js'.format(current_dir)
 
 
def get_rain_level_from_weather(weather):
    rain = False
    rain_level = 0
    if len(weather) > 0:
        for w in weather:
            if w['icon'] == '09d':
                rain = True
                rain_level = 1
            elif w['icon'] == '10d':
                rain = True
                rain_level = 2
            elif w['icon'] == '11d':
                rain = True
                rain_level = 3
            elif w['icon'] == '13d':
                rain = True
                rain_level = 4
 
    return rain, rain_level
 
 
def openweathermap():
    data = {}
    r = requests.get(
        "http://api.openweathermap.org/data/2.5/weather?id=3110044&appid={}&units=metric".format(
            openweathermap_api_key))
 
    if r.status_code == 200:
        current_data = r.json()
        data['weather'] = current_data['main']
        rain, rain_level = get_rain_level_from_weather(current_data['weather'])
        data['weather']['rain'] = rain
        data['weather']['rain_level'] = rain_level
 
    r2 = requests.get(
        "http://api.openweathermap.org/data/2.5/uvi?lat=43.32&lon=-1.93&appid={}".format(openweathermap_api_key))
    if r2.status_code == 200:
        data['uvi'] = r2.json()
 
    r3 = requests.get(
        "http://api.openweathermap.org/data/2.5/forecast?id=3110044&appid={}&units=metric".format(
            openweathermap_api_key))
 
    if r3.status_code == 200:
        forecast = r3.json()['list']
        data['forecast'] = []
        for f in forecast:
            rain, rain_level = get_rain_level_from_weather(f['weather'])
            data['forecast'].append({
                "dt": f['dt'],
                "fields": {
                    "temp": float(f['main']['temp']),
                    "humidity": float(f['main']['humidity']),
                    "rain": rain,
                    "rain_level": int(rain_level),
                    "pressure": float(float(f['main']['pressure']))
                }
            })
 
        return data
 
 
def persists(measurement, fields, location, time):
    logging.info("{} {} [{}] {}".format(time, measurement, location, fields))
    influx_client.write_points([{
        "measurement": measurement,
        "tags": {"location": location},
        "time": time,
        "fields": fields
    }])
 
 
def in_sensors():
    try:
        sense = SenseHat()
        pressure = sense.get_pressure()
        reader_output = check_output([reader, sensor_mac_address]).strip()
        sensor_info = json.loads(reader_output)
        temperature = sensor_info['temperature']
 
        persists(measurement='home_pressure', fields={"value": float(pressure)}, location="in", time=current_time)
        persists(measurement='home_temperature', fields={"value": float(temperature)}, location="in",
                 time=current_time)
    except Exception as err:
        logging.error(err)
 
 
def out_sensors():
    try:
        out_info = openweathermap()
 
        persists(measurement='home_pressure',
                 fields={"value": float(out_info['weather']['pressure'])},
                 location="out",
                 time=current_time)
        persists(measurement='home_humidity',
                 fields={"value": float(out_info['weather']['humidity'])},
                 location="out",
                 time=current_time)
        persists(measurement='home_temperature',
                 fields={"value": float(out_info['weather']['temp'])},
                 location="out",
                 time=current_time)
        persists(measurement='home_rain',
                 fields={"value": out_info['weather']['rain'], "level": out_info['weather']['rain_level']},
                 location="out",
                 time=current_time)
        persists(measurement='home_uvi',
                 fields={"value": float(out_info['uvi']['value'])},
                 location="out",
                 time=current_time)
        for f in out_info['forecast']:
            persists(measurement='home_weather_forecast',
                     fields=f['fields'],
                     location="out",
                     time=datetime.datetime.utcfromtimestamp(f['dt']).isoformat())
 
    except Exception as err:
        logging.error(err)
 
 
influx_client = InfluxDBClient(host=influxdb_host, port=influxdb_port, database=influxdb_database)
current_time = datetime.datetime.utcnow().isoformat()
 
in_sensors()
out_sensors()

Estou rodando esse script Python a partir de um Raspberry Pi3 com um Sense Hat. O Sense Hat tem um sensor de pressão atmosférica, então eu também recuperarei a pressão do Sense Hat.

A partir do openweathermap, vou obter:

  • Temperatura atual/umidade e pressão atmosférica na rua
  • Índice UV (a medida do nível de radiação UV)
  • Condições climáticas (se está chovendo ou não)
  • Previsão do tempo

Eu rodo esse script com o crontab Raspberry Pi a cada cinco minutos; isso significa que tenho uma série temporal pronta para ser exibida com o grafana.

Aqui podemos ver o painel.

O código-fonte está disponível na minha conta do GitHub.

***

Gonzalo Ayuso faz parte do time de colunistas internacionais do iMasters. A tradução do artigo é feita pela Redação iMasters, com autorização do autor, e você pode acompanhar o artigo em inglês no link: https://gonzalo123.com/2018/07/23/playing-with-grafana-and-weather-apis/