ESPhome how to read json from web?

Hello. I would like to make esphome sensor independent on Home Assistant in that it reads weather forecast from openweathermap and controls some switches (roolls up exterior blinds if wind is coming).

I can read forecast by http_request.get and it is in JSON, XML or HTTP format. Can I convert from these formats to variables in esphome, or do I need to do it in C++ lambda?

Thanks, Jan

Paste the response that you’re getting here and we’ll have a shot. Use the preformatted text button in the editor when posting code.

{"your code": "should end up looking like this"}

{“not”: “like this”}

Thanks. The response looks like this, forecast is in section “weather” and I am looking for value “list.wind.speed”:

{"cod":"200","message":0,"cnt":1,"list":[{"dt":1619805600,"main":{"temp":287.83,"feels_like":286.5,"temp_min":284.84,"temp_max":287.83,"pressure":1010,"sea_level":1010,"grnd_level":987,"humidity":44,"temp_kf":2.99},"weather":[{"id":801,"main":"Clouds","description":"few clouds","icon":"02d"}],"clouds":{"all":24},"wind":{"speed":4.43,"deg":18,"gust":8.14},"visibility":10000,"pop":0,"sys":{"pod":"d"},"dt_txt":"2021-04-30 18:00:00"}],"city":{"id":3067696,"name":"Prague","coord":{"lat":50.088,"lon":14.4208},"country":"CZ","population":1165581,"timezone":7200,"sunrise":1619753998,"sunset":1619806732}}

Solved it! Following code works. It finds third forecast of hourly wind speed, which is 1-2 hours ahead of current time:

(please note that I was getting some crashes on ESP8266, but not on ESP32. Seems to be memory problem)

sensor:
  - platform: template
    name: "Predicted wind"
    id: predicted_wind

http_request:
  useragent: esphome/device
  timeout: 10s

time:
  - platform: sntp
    on_time:
      - seconds: 0
        minutes: /15
        then:
          - lambda: |-
              HTTPClient http;
              http.begin("http://api.openweathermap.org/data/2.5/onecall?lat=51&lon=14&appid=974---------------8c0d&lang=cz&exclude=minutely,daily,alerts");
              http.GET();
              DynamicJsonBuffer doc(31000); //cannot be smaller, response is big!
              JsonObject& root = doc.parseObject(http.getStream());
              float ws = atof(root["hourly"][2]["wind_speed"].as<char*>());
              id(predicted_wind).publish_state(ws);
              //ESP_LOGD("main", "############## %s", root["hourly"][2]["wind_speed"].as<char*>()); //.as<char*>());
4 Likes

Great job! These little devices really can do a lot, can’t they?

Is this code up to date with the last version? I’m getting an error

esphome.yaml:119:7: error: 'HTTPClient' was not declared in this scope

Do you have this declaration in yaml?

http_request:
  useragent: esphome/device
  timeout: 5s #30s

It’s not working for me, esp is restarting with the following error:

Guru Meditation Error: Core 1 panic’ed (LoadProhibited). Exception was unhandled

Do you use esp32? It did not work for me very well with esp8266.

I am using a esp32.
I’m trying to make a GET request from the following site:
http://ip-api.com/json/

I have a feeling it has to do with the buffer

That should not be a problem give me email and I will send you whole script that uses weather json

Thank you, I sent you a PM.

Where does the DynamicJsonBuffer come from? that’s definitely what’s cousing the trouble.

Hello,

I am interrested also in getting the public IP and displaying as a sensor value. The API I would like to use is https://wasab.is/ but the other mentioned here should work as well. Is it possible to get an example code snippet on how is this done?

Thank You!

Did the following:

http_request:
  esp8266_disable_ssl_support: true

time:
...
      - seconds: 0
        minutes: /15
        then:
          - lambda: |-
              HTTPClient http;
              http.begin("http://wasab.is/json");
              http.GET();
              DynamicJsonBuffer doc(12000);
              JsonObject& root = doc.parseObject(http.getStream());
              float ip = atof(root["ip"].as<char*>());
              id(public_ip_test).publish_state(ip);

sensor:
  - platform: template
    name: "Public IP"
    id: public_ip_test
    icon: mdi:wan

Unfortunately this does return only the first 4 characters from the IP address in the “sensor” value. I don’t have developer experienc so this is somewhat above my knowledge.

@eisengrau Not sure if you ended up getting it solved yourself but the below works.
Main changes is a “text_sensor” rathar than just a sensor and chagning the ip variable to a const char* rather than float. Note I am fairly new to C++ so there may be a more correct way

http_request:
  esp8266_disable_ssl_support: true

time:
...
      - seconds: 0
        minutes: /15
        then:
          - lambda: |-
              HTTPClient http;
              http.begin("http://wasab.is/json");
              http.GET();
              DynamicJsonBuffer doc(12000);
              JsonObject& root = doc.parseObject(http.getStream());
              const char* ip = root["ip"].as<char*>();
              id(public_ip_test).publish_state(ip);

text_sensor:
  - platform: template
    name: "Public IP"
    id: public_ip_test
    icon: mdi:wan

Hello janbenes
great job.
Can i use your support for extract “description” field in json array ?
Sorry but i am not expert in lambda language.
This is the json complete:

{"coord":{"lon":14,"lat":51},"weather":[{"id":800,"main":"Clear","description":"cielo sereno","icon":"01n"}],"base":"stations","main":{"temp":-0.75,"feels_like":-0.75,"temp_min":-2.12,"temp_max":0.64,"pressure":1022,"humidity":89},"visibility":10000,"wind":{"speed":0.45,"deg":337,"gust":0.89},"clouds":{"all":10},"dt":1646435168,"sys":{"type":2,"id":2008644,"country":"DE","sunrise":1646458837,"sunset":1646499047},"timezone":3600,"id":2867920,"name":"Mühlsdorf","cod":200}


Where can i find some documentation to study this function
Thanks a lot for your support

[SOLVED]

I used the HTTP Request docs on ESPHome.io as a basis and ended up with this (simpler) code:

http_request:
  useragent: esphome/esp32dev
  id: http_request_data

sensor:
  - platform: template
    name: "Air temperature"
    id: air_temp

time:
  - platform: sntp
    id: sntp_time
    on_time:
      # Every 5 minutes
      - seconds: 0
        minutes: /5
        then:
          - http_request.get:
              url: https://api.openweathermap.org/data/2.5/weather?q=oulu&appid=******************************************&units=metric
              headers:
                Content-Type: application/json
              verify_ssl: false
              on_response:
                then:
                  - lambda: |-
                      json::parse_json(id(http_request_data).get_string(), [](JsonObject root) {
                        id(air_temp).publish_state(root["main"]["temp"]);
                      });

Every five minutes, it fetches my local weather info from OpenWeather and extracts the air temperature from the JSON object. It works to perfection.

2 Likes

Hello, sorry for the late reply. Unfortunately this did not work for me. I’m getting the following errors before complie:

/root/esphome/iot-esp8266-test.yaml:80:11: warning: unknown escape sequence: '\/'
   80 |               DynamicJsonBuffer doc(12000);
      |           ^   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~                                                                                                                           
/root/esphome/iot-esp8266-test.yaml:80:11: error: DynamicJsonBuffer is a class from ArduinoJson 5. Please see https://arduinojson.org/upgrade to learn how to upgrade your program to ArduinoJson version 6
Compiling .pioenvs/iot-esp8266-test/lib4d9/ESP8266WiFi/CertStoreBearSSL.cpp.o
/root/esphome/iot-esp8266-test.yaml: In lambda function:
/root/esphome/iot-esp8266-test.yaml:80:25: error: 'doc' was not declared in this scope
   80 |               DynamicJsonBuffer doc(12000);
      |                         ^~~

This is with latest ESPhome 2022.5.1.

Disregard, my last comment. Managed to do it by @epollari 's example above :slight_smile: My final config looks like:

# Time config & External IP

http_request:

  esp8266_disable_ssl_support: true
  useragent: esphome/esp32dev
  id: http_request_test

time:

  - platform: homeassistant
    timezone: Europe/Amsterdam
    
    on_time:
      - seconds: 0
        minutes: /30
        then:
          - http_request.get:
              url: http://wasab.is/json
              headers:
                Content-Type: application/json
              verify_ssl: false
              on_response:
                then:
                  - lambda: |-
                      json::parse_json(id(http_request_test).get_string(),[](JsonObject root){id(public_ip_test).publish_state(root["ip"]);});

text_sensor:

  - platform: template
    name: "Public IP"
    id: public_ip_test
    icon: mdi:wan

Thank you for your awesomeness! :slight_smile:

1 Like