Files
esphome-dev/esphome/components/http_request/__init__.py
T
Otto Winter ac0d921413 ESP-IDF support and generic target platforms (#2303)
* Socket refactor and SSL

* esp-idf temp

* Fixes

* Echo component and noise

* Add noise API transport support

* Updates

* ESP-IDF

* Complete

* Fixes

* Fixes

* Versions update

* New i2c APIs

* Complete i2c refactor

* SPI migration

* Revert ESP Preferences migration, too complex for now

* OTA support

* Remove echo again

* Remove ssl again

* GPIOFlags updates

* Rename esphal and ICACHE_RAM_ATTR

* Make ESP32 arduino compilable again

* Fix GPIO flags

* Complete pin registry refactor and fixes

* Fixes to make test1 compile

* Remove sdkconfig file

* Ignore sdkconfig file

* Fixes in reviewing

* Make test2 compile

* Make test4 compile

* Make test5 compile

* Run clang-format

* Fix lint errors

* Use esp-idf APIs instead of btStart

* Another round of fixes

* Start implementing ESP8266

* Make test3 compile

* Guard esp8266 code

* Lint

* Reformat

* Fixes

* Fixes v2

* more fixes

* ESP-IDF tidy target

* Convert ARDUINO_ARCH_ESPxx

* Update WiFiSignalSensor

* Update time ifdefs

* OTA needs millis from hal

* RestartSwitch needs delay from hal

* ESP-IDF Uart

* Fix OTA blank password

* Allow setting sdkconfig

* Fix idf partitions and allow setting sdkconfig from yaml

* Re-add read/write compat APIs and fix esp8266 uart

* Fix esp8266 store log strings in flash

* Fix ESP32 arduino preferences not initialized

* Update ifdefs

* Change how sdkconfig change is detected

* Add checks to ci-custom and fix them

* Run clang-format

* Add esp-idf clang-tidy target and fix errors

* Fixes from clang-tidy idf round 2

* Fixes from compiling tests with esp-idf

* Run clang-format

* Switch test5.yaml to esp-idf

* Implement ESP8266 Preferences

* Lint

* Re-do PIO package version selection a bit

* Fix arduinoespressif32 package version

* Fix unit tests

* Lint

* Lint fixes

* Fix readv/writev not defined

* Fix graphing component

* Re-add all old options from core/config.py

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-09-20 11:47:51 +02:00

187 lines
6.0 KiB
Python

import urllib.parse as urlparse
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation
from esphome.const import (
CONF_ID,
CONF_TIMEOUT,
CONF_METHOD,
CONF_TRIGGER_ID,
CONF_URL,
CONF_ESP8266_DISABLE_SSL_SUPPORT,
)
from esphome.core import Lambda, CORE
DEPENDENCIES = ["network"]
AUTO_LOAD = ["json"]
http_request_ns = cg.esphome_ns.namespace("http_request")
HttpRequestComponent = http_request_ns.class_("HttpRequestComponent", cg.Component)
HttpRequestSendAction = http_request_ns.class_(
"HttpRequestSendAction", automation.Action
)
HttpRequestResponseTrigger = http_request_ns.class_(
"HttpRequestResponseTrigger", automation.Trigger
)
CONF_HEADERS = "headers"
CONF_USERAGENT = "useragent"
CONF_BODY = "body"
CONF_JSON = "json"
CONF_VERIFY_SSL = "verify_ssl"
CONF_ON_RESPONSE = "on_response"
def validate_url(value):
value = cv.string(value)
try:
parsed = list(urlparse.urlparse(value))
except Exception as err:
raise cv.Invalid("Invalid URL") from err
if not parsed[0] or not parsed[1]:
raise cv.Invalid("URL must have a URL scheme and host")
if parsed[0] not in ["http", "https"]:
raise cv.Invalid("Scheme must be http or https")
if not parsed[2]:
parsed[2] = "/"
return urlparse.urlunparse(parsed)
def validate_secure_url(config):
url_ = config[CONF_URL]
if (
config.get(CONF_VERIFY_SSL)
and not isinstance(url_, Lambda)
and url_.lower().startswith("https:")
):
raise cv.Invalid(
"Currently ESPHome doesn't support SSL verification. "
"Set 'verify_ssl: false' to make insecure HTTPS requests."
)
return config
CONFIG_SCHEMA = cv.All(
cv.Schema(
{
cv.GenerateID(): cv.declare_id(HttpRequestComponent),
cv.Optional(CONF_USERAGENT, "ESPHome"): cv.string,
cv.Optional(
CONF_TIMEOUT, default="5s"
): cv.positive_time_period_milliseconds,
cv.SplitDefault(CONF_ESP8266_DISABLE_SSL_SUPPORT, esp8266=False): cv.All(
cv.only_on_esp8266, cv.boolean
),
}
).extend(cv.COMPONENT_SCHEMA),
cv.require_framework_version(
esp8266_arduino=cv.Version(2, 5, 1),
esp32_arduino=cv.Version(0, 0, 0),
),
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
cg.add(var.set_timeout(config[CONF_TIMEOUT]))
cg.add(var.set_useragent(config[CONF_USERAGENT]))
if CORE.is_esp8266 and not config[CONF_ESP8266_DISABLE_SSL_SUPPORT]:
cg.add_define("USE_HTTP_REQUEST_ESP8266_HTTPS")
await cg.register_component(var, config)
HTTP_REQUEST_ACTION_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.use_id(HttpRequestComponent),
cv.Required(CONF_URL): cv.templatable(validate_url),
cv.Optional(CONF_HEADERS): cv.All(
cv.Schema({cv.string: cv.templatable(cv.string)})
),
cv.Optional(CONF_VERIFY_SSL, default=True): cv.boolean,
cv.Optional(CONF_ON_RESPONSE): automation.validate_automation(
{cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(HttpRequestResponseTrigger)}
),
}
).add_extra(validate_secure_url)
HTTP_REQUEST_GET_ACTION_SCHEMA = automation.maybe_conf(
CONF_URL,
HTTP_REQUEST_ACTION_SCHEMA.extend(
{
cv.Optional(CONF_METHOD, default="GET"): cv.one_of("GET", upper=True),
}
),
)
HTTP_REQUEST_POST_ACTION_SCHEMA = automation.maybe_conf(
CONF_URL,
HTTP_REQUEST_ACTION_SCHEMA.extend(
{
cv.Optional(CONF_METHOD, default="POST"): cv.one_of("POST", upper=True),
cv.Exclusive(CONF_BODY, "body"): cv.templatable(cv.string),
cv.Exclusive(CONF_JSON, "body"): cv.Any(
cv.lambda_,
cv.All(cv.Schema({cv.string: cv.templatable(cv.string_strict)})),
),
}
),
)
HTTP_REQUEST_SEND_ACTION_SCHEMA = HTTP_REQUEST_ACTION_SCHEMA.extend(
{
cv.Required(CONF_METHOD): cv.one_of(
"GET", "POST", "PUT", "DELETE", "PATCH", upper=True
),
cv.Exclusive(CONF_BODY, "body"): cv.templatable(cv.string),
cv.Exclusive(CONF_JSON, "body"): cv.Any(
cv.lambda_,
cv.All(cv.Schema({cv.string: cv.templatable(cv.string_strict)})),
),
}
)
@automation.register_action(
"http_request.get", HttpRequestSendAction, HTTP_REQUEST_GET_ACTION_SCHEMA
)
@automation.register_action(
"http_request.post", HttpRequestSendAction, HTTP_REQUEST_POST_ACTION_SCHEMA
)
@automation.register_action(
"http_request.send", HttpRequestSendAction, HTTP_REQUEST_SEND_ACTION_SCHEMA
)
async def http_request_action_to_code(config, action_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, paren)
template_ = await cg.templatable(config[CONF_URL], args, cg.std_string)
cg.add(var.set_url(template_))
cg.add(var.set_method(config[CONF_METHOD]))
if CONF_BODY in config:
template_ = await cg.templatable(config[CONF_BODY], args, cg.std_string)
cg.add(var.set_body(template_))
if CONF_JSON in config:
json_ = config[CONF_JSON]
if isinstance(json_, Lambda):
args_ = args + [(cg.JsonObjectRef, "root")]
lambda_ = await cg.process_lambda(json_, args_, return_type=cg.void)
cg.add(var.set_json(lambda_))
else:
for key in json_:
template_ = await cg.templatable(json_[key], args, cg.std_string)
cg.add(var.add_json(key, template_))
for key in config.get(CONF_HEADERS, []):
template_ = await cg.templatable(
config[CONF_HEADERS][key], args, cg.const_char_ptr
)
cg.add(var.add_header(key, template_))
for conf in config.get(CONF_ON_RESPONSE, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID])
cg.add(var.register_response_trigger(trigger))
await automation.build_automation(trigger, [(int, "status_code")], conf)
return var