Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/esphome/esphome.io/llms.txt

Use this file to discover all available pages before exploring further.

The ESPHome web server exposes two complementary APIs on your device’s IP address: a real-time Server-Sent Events (SSE) stream at /events and a conventional REST API for reading states and issuing commands. Both APIs are enabled automatically when you add the web_server: component to your configuration — no extra setup is required beyond turning it on.
The web server is intentionally limited to viewing and controlling entity states. It does not and will not support changing device configuration (e.g., WiFi credentials or firmware settings), as that would require significantly more flash and RAM.
To navigate to the built-in web frontend, open your browser at http://<device-ip>/ or use the mDNS hostname http://<name>.local/. For a device named livingroom, that is http://livingroom.local/.

Enabling the Web Server

Add the web_server: block to your ESPHome YAML:
web_server:
  port: 80
All REST and SSE endpoints described on this page become available immediately after flashing.

Event Source API

The SSE endpoint at /events delivers real-time state updates using the EventSource protocol (also known as Server-Sent Events). Connect to it from any browser, Node.js app, or Python script to receive a continuous stream of device events without polling.

Event Types

There are three event types:
TypePurpose
pingSent periodically to keep the connection alive. No payload.
stateSent whenever a component’s state changes. Contains a JSON payload.
logSent on every firmware log message. Used by the built-in web frontend.
When a client first connects, the server replays the current state of all entities so the client immediately has a consistent snapshot.

State Event Payload

Every state event carries a JSON object with at minimum the following identifier fields:
FieldDescription
name_idTemporary field (removed in 2026.8.0). New identifier format: domain/entity_name (e.g., sensor/Temperature) or domain/device_name/entity_name for sub-device entities.
idLegacy identifier in domain-object_id format (e.g., sensor-temperature). In ESPHome 2026.8.0 this field switches to the new format and name_id is removed.
stateText representation of the current state (e.g., ON, 21.4 °C).
Third-party integrations must prefer name_id over id today to get the new ID format, falling back to id for compatibility with older firmware. After ESPHome 2026.8.0, id will carry the new format and name_id will be removed. Plan your migration accordingly.

JavaScript Example

const evtSource = new EventSource("http://livingroom.local/events");

evtSource.addEventListener("state", (event) => {
  const data = JSON.parse(event.data);
  // Prefer name_id for forward compatibility
  const entityId = data.name_id ?? data.id;
  console.log(`${entityId}: ${data.state}`);
});

evtSource.addEventListener("ping", () => {
  console.log("Connection alive");
});

evtSource.onerror = () => {
  console.error("SSE connection error");
};

REST API

The REST API follows the URL schema:
/<domain>/<entity_name>[/<action>?<param>=<value>]
For entities on sub-devices:
/<domain>/<device_name>/<entity_name>[/<action>]
  • domain — the component type: sensor, switch, light, cover, button, number, select, fan, binary_sensor, alarm_control_panel
  • entity_name — the name: value exactly as configured in YAML, including spaces and UTF-8 characters
  • actionturn_on, turn_off, toggle, set, press, etc.

URL Format and HTTP Methods

SegmentsGETPOST
2: /{domain}/{entity}Returns entity stateN/A
3: /{domain}/{X}/{Y}Sub-device state (X=device, Y=entity)Main device action (X=entity, Y=action)
4: /{domain}/{device}/{entity}/{action}InvalidSub-device action
The legacy URL format using object_id (lowercase with underscores, e.g., /sensor/temperature_sensor) is still supported but deprecated and will be removed in ESPHome 2026.7.0.

Sensor

Sensors are read-only — only GET is supported. GET /sensor/<entity_name>
curl http://livingroom.local/sensor/Outside%20Temperature
{
  "id": "sensor/Outside Temperature",
  "state": "19.8 °C",
  "value": 19.76666
}
Add ?detail=all to include extended information such as name and device:
curl "http://livingroom.local/sensor/Garage/Temperature?detail=all"
{
  "id": "sensor/Garage/Temperature",
  "name": "Temperature",
  "device": "Garage",
  "state": "15.2 °C",
  "value": 15.23
}

Binary Sensor

Binary sensors are also read-only. GET /binary_sensor/<entity_name>
curl http://livingroom.local/binary_sensor/Living%20Room%20Status
{
  "id": "binary_sensor/Living Room Status",
  "state": "ON",
  "value": true
}

Switch

Switches report state identically to binary sensors and additionally accept POST commands. GET /switch/<entity_name> — returns current state (ON/OFF) POST /switch/<entity_name>/turn_on — turns the switch on POST /switch/<entity_name>/turn_off — turns the switch off POST /switch/<entity_name>/toggle — toggles the switch
# Read state
curl http://livingroom.local/switch/Dehumidifier

# Turn on
curl -X POST http://livingroom.local/switch/Dehumidifier/turn_on

# Turn off
curl -X POST http://livingroom.local/switch/Dehumidifier/turn_off

Light

GET /light/<entity_name>
curl http://livingroom.local/light/Living%20Room%20Lights
{
  "id": "light/Living Room Lights",
  "state": "ON",
  "brightness": 255,
  "color": { "r": 255, "g": 255, "b": 255 },
  "effect": "None",
  "white_value": 255
}
POST /light/<entity_name>/turn_on — optional URL parameters:
ParameterTypeDescription
brightness0–255Light brightness
r0–255Red channel
g0–255Green channel
b0–255Blue channel
white_value0–255White channel (RGBW lights)
color_tempmiredsColor temperature
transitionsecondsTransition duration
flashsecondsFlash duration
effectstringEffect name
POST /light/<entity_name>/turn_off?transition=2 — fade off over 2 seconds POST /light/<entity_name>/toggle
# Dim to 50% over 2 seconds
curl -X POST "http://livingroom.local/light/Living%20Room%20Lights/turn_on?brightness=128&transition=2"

# Set warm white color temperature
curl -X POST "http://livingroom.local/light/Living%20Room%20Lights/turn_on?color_temp=400"

Cover

GET /cover/<entity_name>
curl http://livingroom.local/cover/Front%20Window%20Blinds
{
  "id": "cover/Front Window Blinds",
  "state": "OPEN",
  "value": 0.8,
  "current_operation": "IDLE",
  "tilt": 0.5
}
POST methods: open, close, stop, toggle, set
ParameterDescription
positionTarget position 0.0 (closed) to 1.0 (open) for set
tiltTilt angle 0.0–1.0, if supported
# Move to 10% open with tilt at 30%
curl -X POST "http://livingroom.local/cover/Front%20Window%20Blinds/set?position=0.1&tilt=0.3"

# Stop mid-travel
curl -X POST http://livingroom.local/cover/Front%20Window%20Blinds/stop

Button

Buttons can only be pressed — no state is stored. POST /button/<entity_name>/press
curl -X POST http://livingroom.local/button/Do%20Something/press

Number

GET /number/<entity_name>
{
  "id": "number/Desired Delay",
  "state": "20.0000",
  "value": 20
}
POST /number/<entity_name>/set?value=<n> — set the number to n (must be within the component’s min/max range)
curl -X POST "http://livingroom.local/number/Desired%20Delay/set?value=24"

Select

GET /select/<entity_name> — returns current option GET /select/<entity_name>?detail=all — returns current option plus all available options
{
  "id": "select/House Mode",
  "name": "House Mode",
  "state": "party",
  "value": "party",
  "option": ["party", "sleep", "relax", "home", "away"]
}
POST /select/<entity_name>/set?option=<value> — set the selected option
curl -X POST "http://livingroom.local/select/House%20Mode/set?option=home"

Fan

GET /fan/<entity_name>
{
  "id": "fan/Living Room Fan",
  "state": "ON",
  "value": true,
  "speed_level": 2,
  "oscillation": false
}
POST methods: turn_on, turn_off, toggle turn_on optional parameters: speed_level (integer), oscillation (boolean)
curl -X POST "http://livingroom.local/fan/Living%20Room%20Fan/turn_on?speed_level=3"

Alarm Control Panel

GET /alarm_control_panel/<entity_name>
{
  "id": "alarm_control_panel/My Alarm",
  "state": "ARMED_AWAY",
  "value": 2
}
Possible states: DISARMED, ARMED_HOME, ARMED_AWAY, ARMED_NIGHT, ARMED_VACATION, ARMED_CUSTOM_BYPASS, PENDING, ARMING, DISARMING, TRIGGERED POST methods: arm_away, arm_home, arm_night, arm_vacation, disarm The optional code parameter should be sent in POST body data rather than in the URL query string to avoid logging credentials.

Authentication

If you configured auth: in web_server:, all requests require HTTP Basic Authentication:
curl -u "admin:mysecret" http://livingroom.local/sensor/Temperature
# Example web_server config with auth
web_server:
  port: 80
  auth:
    username: admin
    password: mysecret

Build docs developers (and LLMs) love