diff --git a/content/components/_index.md b/content/components/_index.md index 2944f894a7..fcc7bd31d0 100644 --- a/content/components/_index.md +++ b/content/components/_index.md @@ -104,6 +104,7 @@ ESPHome-specific components or components supporting ESPHome device provisioning {{< imgtable >}} "Network Core","components/network","server-network.svg","dark-invert" "Native API","components/api","server-network.svg","dark-invert" +"Coap Client","components/coap_client","connection.svg","dark-invert" "MQTT","components/mqtt","mqtt.png","" "ESP-NOW","components/espnow","esp-now.svg","" "HTTP Request","components/http_request","connection.svg","dark-invert" diff --git a/content/components/coap_client.md b/content/components/coap_client.md new file mode 100644 index 0000000000..30bc06ebeb --- /dev/null +++ b/content/components/coap_client.md @@ -0,0 +1,339 @@ +--- +description: "Instructions for setting up CoAP Clients in ESPHome" +title: "CoAP Client" +params: + seo: + description: "A component that enables ESPHome devices to send CoAP (RFC7252) GET/POST requests to remote CoAP servers." + image: connection.svg +--- + +The `coap_client` component lets you send CoAP (RFC7252) GET/POST requests to remote CoAP servers. + +```yaml +# Example configuration entry +coap_client: +``` + +## Overview + +The **CoAP Client** component allows an ESPHome device to send [CoAP](https://datatracker.ietf.org/doc/html/rfc7252) messages to remote CoAP servers over UDP. +This is useful for integrating ESPHome devices with: + +- Constrained IoT devices/sensors +- CoAP-enabled home automation systems +- Local microcontrollers using lightweight UDP messaging +- Any endpoint that exposes CoAP resources (GET/POST/etc.) + +This component uses [libCoap](https://libcoap.net/) via [espressif/coap](https://components.espressif.com/components/espressif/coap/versions/4.3.5~3/readme). Please refer to the links for all license requirements + +This component provides an easy YAML interface and Actions usable from automations and lambda blocks. + +{{< anchor "coap_client-configuration_variables" >}} + +## Configuration variables + +- **id** (*Optional*, [ID](/guides/configuration-types#id)): Manually specify the ID used for code generation. +- **max_block_size** (*Optional*, bytes): Maximum size of payload for a given datagram packet. Packet size is larger since it includes the header. Maxium value is 1024B, defaults to 512B. +- **request_timeout** (*Optional*, duration): Time client will block while waiting for response within the processing thread, defaults to 2 seconds. +- **ack_timeout** (*Optional*, duration): The initial number of seconds to wait for an acknowledgment (ACK) or a response to a Confirmable (CON) message,defaults to 2 seconds. +- **max_retransmit** (*Optional*, integers): The maximum number of times a Confirmable message is retransmitted before the library stops sending it and signals a failure, defaults to 4 attempts. + +## Actions + +The `coap_client` component supports a number of [actions](/automations/actions#all-actions) that can be used to send coap requests. + +{{< anchor "coap_client-get_action" >}} + +### `coap_client.get` Action + +This [action](/automations/actions#all-actions) sends a GET Request. Providing a request_name allows the later use of stop (when observe = true), resume (observe = true), and remove. +If you send again to the same request_name, then the stored request is overwritten and the CoAP Session is reused. It is the responsibility of the caller to ensure the url does not change scheme or destination and that the resource +does not require a different credential requirement. + +If request_name is not provided, a temporary name is assigned and the request is deleted from storage after timeout or response. In the case of observe=true, the request is stored until timeout or stop action with pause = false. + +```yaml +on_...: + then: + - coap_client.get: + url: coap://example-server.com/test + # Short form + - coap_client.get: coap://example-server.com/test +``` + +#### Configuration variables + +- **url** (**Required**, string, [templatable](/automations/templates)): URL to which to send the Request. +- **request_name** (**Optional**, string): Name the request, usable when removing the observe Request. +- **capture_response** (*Optional*, boolean): when set to `true`, the response data will be captured and placed into + the `payload` variable as a `std::string` for use in [lambdas](/automations/templates#config-lambda). Defaults to `false`. +- **response_timeout** (*Optional*, duration): Time client will wait for a response before removing the request. Defaults to 4 seconds. +- **observe** (*Optional*, boolean): when set to `true` and url points to an observable resource, the system will continue to accept responses +- **max_response_buffer_size** (*Optional*, integer): The maximum buffer size to be used to store the response. Defaults to `1 kB`. +- **on_response** (*Optional*, [Automation](/automations)): An automation to perform after the Request is received. +- **on_error** (*Optional*, [Automation](/automations)): An automation to perform if the Request cannot be completed. + +{{< anchor "coap_client-post_action" >}} + +### `coap_client.post` Action + +This [action](/automations/actions#all-actions) sends a POST Request. + +```yaml +on_...: + then: + - coap_client.post: + request_name: "post example" + url: coap://example-server.com/test + payload:"this is a post" + # Short form + - coap_client.post: CoAPs://esphome.io +``` + +#### Configuration variables + +- **media_type** (*Optional, string): Type of payload sent, defaults to text/plain or application/json. +- **payload** (*Optional*, string, [templatable](/automations/templates)): A CoAP payload string to send with Request. +- **json** (*Optional*, mapping): A CoAP payload in JSON format. Values are [templatable](/automations/templates). +- All other options from [`coap_client.get` Action](#coap_client-get_action). + +{{< anchor "coap_client-send_action" >}} + +### `coap_client.send` Action + +This [action](/automations/actions#all-actions) sends a Request. + +```yaml +on_...: + then: + - coap_client.send: + request_name: "send example" + method: PUT + media_type: "application/json" + url: coap://example-server.com/test +``` + +#### Configuration variables + +- **method** (**Required**, string): CoAP method to use (`GET`, `POST`, `PUT`, `DELETE`, `FETCH`, `PATCH`, `IPATCH` ). +- All other options from [`coap_client.post` Action](#coap_client-post_action) and [`coap_client.get` Action](#coap_client-get_action). + +{{< anchor "coap_client-stop_action" >}} + +### `coap_client.stop` Action + +This [action](/automations/actions#all-actions) stops an observe Request + +```yaml +on_...: + then: + - coap_client.stop: + request_name: "Test Observe" + # keep in storage + pause: true + # Short form + - coap_client.stop: "Test Observe" +``` + +#### Configuration variables + +- **request_name** (**Required**, string): Name the request, usable when removing the observe Request. +- **pause** (**Optional**, bool): if true, keep request in storage for future resume or send. Defaults to True + +{{< anchor "coap_client-resume_action" >}} + +### `coap_client.resume` Action + +This [action](/automations/actions#all-actions) resumes an observe Request that was pause=true when stopped. + +```yaml +on_...: + then: + - coap_client.resume: + request_name: "Test Observe" + # Short form + - coap_client.resume: "Test Observe" +``` + +#### Configuration variables + +- **request_name** (**Required**, string): Name the request, usable when removing the observe Request. If not provided, then all observe requests are removed. + +{{< anchor "coap_client-remove_action" >}} + +### `coap_client.remove` Action + +This [action](/automations/actions#all-actions) removes a stored request, if request is observe=true, then it must be stopped prior to remove. + +```yaml +on_...: + then: + - coap_client.remove: + request_name: "Test Observe" + # Short form + - coap_client.remove: "Test Observe" +``` + +#### Configuration variables + +- **request_name** (**Required**, string): Name the request, usable when removing the observe Request. If not provided, then all observe requests are removed. + +## Triggers + +{{< anchor "coap_client-on_response" >}} + +### `on_response` Trigger + +This automation will be triggered when the CoAP Client is complete. If the Request had observe set to `true` and the server resource was observable, then it will keep responding on a schedule defined by the server. +The following variables are available for use in [lambdas](/automations/templates#config-lambda): + +- `response` as a pointer to `CoapResponseStatistics` object which contains `content_length`, `status_code` and `duration_ms``. +- `request_name` as std::string, an unique number as a std::string is provided when no request_name is configured. +- `payload` as `std::string` which contains the response payload when `capture_response` + (see [`coap_client.get` Action](#coap_client-get_action)) is set to `true`. + +> [!NOTE] +> The `status_code` should be checked before using the `payload` variable. A successful response will have +> a status code in the `200's`. Server errors such as "not found" (404) or "internal server error" (500) will have an appropriate status code, and may contain an error message in the `payload` variable. + +```yaml +on_...: + then: + - delay: 5 sec + - coap_client.get: + request_name: "Test Observe" + url: coap://example-server.com/obs + capture_response: true + response_timeout: 10sec + observe: true + on_response: + then: + - logger.log: + format: '%s Response status: %d, Duration: %u ms, %s' + args: + - request_name.c_str() + - response->status_code + - response->duration_ms + - payload.c_str() +``` + +### POST Payload in JSON format (syntax 1) + +**Note:** all values of the map must be strings. It is not possible to send JSON `boolean` or `numbers` with this +syntax. + +```yaml +on_...: + - coap_client.post: + url: coap://example-server.com/test + json: + key: !lambda |- + return id(my_sensor).state; + greeting: "Hello World" + + # Will send: + # {"key": "42.0", "greeting": "Hello World"} +``` + +### POST Payload in JSON format (syntax 2) + +**Note:** use this syntax to send `boolean` or `numbers` in JSON. + +The JSON message will be constructed using the [ArduinoJson](CoAPs://github.com/bblanchon/ArduinoJson) library. +In the `json` option you have access to a `root` object which represents the base object of the JSON message. You +can assign values to keys by using the `root["KEY_NAME"] = VALUE;` syntax as shown below. + +```yaml +on_...: + - coap_client.post: + url: coap://example-server.com/test + json: |- + root["key"] = id(my_sensor).state; + root["greeting"] = "Hello World"; + + # Will send: + # {"key": 42.0, "greeting": "Hello World"} +``` + +### GET values from a JSON payload response + +If you want to retrieve the value for the vol key and assign it to a template sensor or number component whose id is +set to player_volume you can do this, but note that checking for the presence of the key will prevent difficult-to-read +error messages: + +This example assumes that the server returns a response as a JSON object similar to this: +`{"status":"play","vol":"42","mute":"0"}` + +If you want to retrieve the value for the `vol` key and assign it to a template `sensor` or `number` component +whose `id` is set to `player_volume` : + +```yaml +on_...: +- coap_client.get: + url: coap://example-server.com/test + capture_response: true + on_response: + then: + - if: + condition: + lambda: return response->status_code == 205; + then: + - lambda: |- + json::parse_json(payload, [](JsonObject root) -> bool { + if (root["vol"]) { + id(player_volume).publish_state(root["vol"]); + return true; + } + else { + ESP_LOGI(TAG,"No 'vol' key in this json!"); + return false; + } + }); + else: + - logger.log: + format: "Error: Response status: %d, message %s" + args: [ 'response->status_code', 'payload.c_str()' ] +``` + +## Programatic Integration + +Example code for usage within another component + +```cpp + //unique_ptr is required + std::unique_ptr tx_request = std::make_unique(); + tx_request->name = this->request_name; + tx_request->method = CoapMethod::POST + tx_request->uri = this->url; + tx_request->callback = callback; + tx_request->callback_context = this; + tx_request->media_type = CoapMediaType::TEXT_PLAIN; + tx_request->payload = this->payload; + tx_request->response_timeout = this->response_timeout; + tx_request->observe = this->observe; + global_coap_client->process_request(std::move(tx_request)); // Move is required! +``` + +Create a callback for the response information + +- **response_code** - 205 is good!, anything outside of 200's is an error. 0 is response timeout. +- **data** - payload returned. +- **len** - of the data. +- **offset** - in case the data is returned in blocks. +- **total** - isn't trustworthy till last block when len + offset = total. +- **context** - the provided callback_context. + +If len, offset, and total are all 0 when there is a response timeout or other response error. +if response is in blocks, then your code is responsible for assembly. + +```cpp +static void callback(uint16_t response_code, const unsigned char *data, size_t len, size_t offset, size_t total, void *context) { + \\... Your Code Here +} +``` + +## See Also + +- {{< docref "index/" >}} +- {{< apiref "coap_client/coap_client.h" "coap_client/coap_client.h" >}} +- {{< docref "/components/json" >}} diff --git a/content/components/ota/coap_client.md b/content/components/ota/coap_client.md new file mode 100644 index 0000000000..1b7b150ee4 --- /dev/null +++ b/content/components/ota/coap_client.md @@ -0,0 +1,106 @@ +--- +description: "Instructions for setting up Over-The-Air (OTA) updates for ESPs to download firmwares remotely by CoAP." +title: "OTA Update via CoAP Client" +params: + seo: + description: Instructions for setting up Over-The-Air (OTA) updates for ESPs to download firmwares remotely by CoAP. + image: system-update.svg +--- + +The OTA (Over The Air) via CoAP Client update component allows your devices to install updated firmware on their own. +To use it, in your device's configuration, you specify a URL from which the device will download the binary +file (firmware). To trigger the update, an ESPHome [action](/automations/actions#all-actions) is used which initiates the +download and installation of the new firmware. Once complete, the device is rebooted, invoking the new firmware. + +Since the device functions as a CoAP(S) client, it can be on a foreign network or behind a firewall. + +To use this platform, the {{< docref "coap_client/" >}} component must be present in your configuration. + +```yaml +# Example configuration entry +ota: + - platform: coap_client +``` + +## Configuration variables + +- All [automations](/automations) supported by {{< docref "/components/ota" >}}. + +{{< anchor "ota_coap_client-flash_action" >}} + +## `ota.coap_client.flash` Action + +This action triggers the download and installation of the updated firmware from the configured URL. As it's an +ESPHome [action](/automations/actions#all-actions), it may be used in any ESPHome [automation(s)](/automations). + +```yaml +on_...: + then: + - ota.coap_client.flash: + md5_url: coap://example.com/firmware.md5 + url: coap://example.com/firmware.ota.bin + - logger.log: "This message should be not displayed because the device reboots" +``` + +### Configuration variables + +- **md5** (*Optional*, string, [templatable](/automations/templates)): The + [MD5sum](https://en.wikipedia.org/wiki/Md5sum) of the firmware file pointed to by `url` (below). May not be used + with `md5_url` (below); must be specified if `md5_url` is not. + +- **md5_url** (*Optional*, string, [templatable](/automations/templates)): The URL of the file containing an + [MD5sum](https://en.wikipedia.org/wiki/Md5sum) of the firmware file pointed to by `url` (below). May not be used + with `md5` (above); must be specified if `md5` is not. + +- **url** (**Required**, string, [templatable](/automations/templates)): The URL of the binary file containing the + (new) firmware to be installed. + +> [!NOTE] +> +> - You can obtain the `firmware.ota.bin` file from either: +> +> - **ESPHome dashboard** (HA add-on): download in *"OTA format"* (formerly "legacy format") +> - **ESPHome CLI**: the directory `.esphome/build/project/.pioenvs/project/firmware.ota.bin` +> +> ...where *"project"* is the name of your ESPHome device/project. +> +> You **cannot** use `firmware.factory.bin` or *"Factory format"* (formerly "Modern format") with this component. +> +> - `username` and `password` must be [URL-encoded](https://en.wikipedia.org/wiki/Percent-encoding) if they +> include special characters. +> +> - The [MD5sum](https://en.wikipedia.org/wiki/Md5sum) of the firmware binary file is an ASCII file (also known +> as "plain text", typically found in files with a `.txt` extension) consisting of 32 lowercase hexadecimal +> characters. It can be obtained and saved to a file with the following command(s): +> +> - On macOS: +> +> ```shell +> md5 -q firmware.ota.bin > firmware.md5 +> ``` +> +> - On most Linux distributions: +> +> ```shell +> md5sum firmware.ota.bin > firmware.md5 +> ``` +> +> - On Windows/PowerShell: +> +> ```shell +> (Get-FileHash -Path firmware.ota.bin -Algorithm md5).Hash.ToLower() | Out-File -FilePath firmware.md5 -Encoding ASCII +> ``` +> +> This will generate the MD5 hash of the `firmware.ota.bin` file and write the resulting hash value to the +> `firmware.md5` file. The `md5_url` configuration variable should point to this file on the web server. +> It is used by the OTA updating mechanism to ensure the integrity of the (new) firmware as it is installed. +> +> **If, for any reason, the MD5sum provided does not match the MD5sum computed as the firmware is installed, the +> device will continue to use the original firmware and the new firmware is discarded.** + +## See Also + +- {{< apiref "ota/ota_component.h" "ota/ota_component.h" >}} +- {{< docref "/components/ota" >}} +- {{< docref "/components/ota/esphome" >}} +- {{< docref "/components/safe_mode" >}} diff --git a/content/components/update/coap_client.md b/content/components/update/coap_client.md new file mode 100644 index 0000000000..5a179b5af5 --- /dev/null +++ b/content/components/update/coap_client.md @@ -0,0 +1,83 @@ +--- +description: "Instructions for using ESPHome's coap_client update platform to manage updates on your devices." +title: "Managed Updates via CoAP Client" +params: + seo: + description: Instructions for using ESPHome's coap_client update platform to manage updates on your devices. + image: system-update.svg +--- + +This platform allows you to manage the deployment of updates to your ESPHome devices. It works by reading a +[JSON manifest file](#update_coap_client-manifest_format) and using it to determine the presence of an update. + +To use it, the following components are required in your device's configuration: + +- {{< docref "/components/coap_client" >}} +- {{< docref "/components/ota/coap_client" >}} + +```yaml +# Example configuration entry +update: + - platform: coap_client + name: Firmware Update + source: coap://example.com/manifest.json + +#below is sample of template button for actioning update +button: + - platform: template + name: "Update from manifest" + on_press: + update.perform +``` + +{{< anchor "update_coap_client-configuration_variables" >}} + +## Configuration variables + +- **source** (**Required**, string): The URL of the YAML manifest file containing the firmware metadata. +- **update_interval** (*Optional*, [Time](/guides/configuration-types#time)): The interval at which to check for (**not install**) updates. + Defaults to 6 hours. + +- All other options from [Update](/components/update#config-update). + +{{< anchor "update_coap_client-manifest_format" >}} + +## Update Manifest Format + +This component expects the [ESP-Web-Tools manifest](coaps://github.com/esphome/esp-web-tools) with an extension in +the `ota` block that is structured as follows: + +```json +{ + "name": "My ESPHome Project", + "version": "2024.6.1", + "builds": [ + { + "chipFamily": "ESP32-C3", + "ota": { + "md5": "1234567890abcdef1234567890abcdef", + "path": "/local/esp32c3/firmware.bin", + "release_url": "coap://example.com/releases/10", + "summary": "Another update" + } + } + ] +} +``` + +While `release_url` and `summary` are optional, all other fields shown here are required. + +If `path` begins with: + +- `coap`: `path` is treated as full URL which will be used to obtain the firmware binary. +- A forward slash (`/` ): `path` will be appended to the hostname (an "absolute" path) specified for `source` (as above). +- Any other character: `path` will be appended to `source` (as specified above) after trimming the manifest file name. + +Note that there may be multiple `builds` specified within a single JSON file. + +## See Also + +- {{< docref "coap_client/" >}} +- {{< docref "/components/ota/coap_client" >}} +- {{< docref "/components/ota" >}} +- {{< apiref "update/update_entity.h" "update/update_entity.h" >}}