Tapo Cameras
TP-Link Tapo cameras, fully local, with detections that come back on their own after a reboot.
Live stream, privacy mode, pan and tilt with presets, detection settings, and motion, person, vehicle, pet… sensors created from the start. Built for the cameras' quirks: one request at a time, events renewed before they expire, no login storms.
Tapo Cameras has no dashboard of its own: it works on the server, with its entities, actions and notifications described in the documentation below.
Highlights
- One camera entity per camera, with a stream quality select (HD or SD)
- Privacy mode shown as a crossed-out camera, its stream stopped
- Detection sensors created at setup for every detection the camera supports
- ONVIF events renewed before they expire and after a reboot, steady detections
- Switches, selects and numbers for the camera's settings, siren, spotlight, firmware update
- Pan and tilt by an angle, presets to go to, save or delete
- DHCP discovery, reconfigure, re-authentication and diagnostics
Works with
Install
- 1Open the repository in the Marketplace with the Add to Marketplace button above, or add
github.com/juju35/ha-tapoas a custom repository (category Integration). - 2Download it from the Marketplace.
- 3Restart Home Assistant and add the integration from Settings → Devices & services.
Documentation
From the README Features
Features
- One camera entity per camera, with a Stream quality select (HD or SD) that switches the running stream; turning the camera off turns privacy mode on
- Privacy mode shown: the camera displays a large crossed-out camera instead of a black or frozen picture, and its live stream stops until privacy mode is turned off
- Detection sensors created at setup, for every detection the camera supports (motion, person, vehicle, pet, line crossing, tamper, baby crying, barking, meowing, glass break, package), before any event happens; a detection the camera reports and this integration doesn't know yet gets a sensor too
- Events that survive reboots: the camera's ONVIF event subscription is renewed before it expires and created again, with a backoff, when the camera drops it; a pull the camera cuts is pulled again (and shortened if the camera doesn't hold long pulls), and a lost subscription is replaced at once, so the sensors only turn unavailable when the camera can't be reached; a Detection events sensor shows whether the subscription is up
- Steady detections: a detection stays on 30 s (configurable) after the camera ends it, so a movement is one detection and one event instead of an on/off every few seconds
- Safety timeout: a detection still on after 60 s without news (configurable) is switched off, for firmwares that never send the end of an event
- One request at a time per camera: Tapo firmwares drop the session when two requests arrive together, so polling and changes share a queue
- One light request per refresh: the settings are read with a single batched request, without the half of it this integration never uses; capabilities, network, SD card and presets are read every 10 minutes, after a change and after a new login only, and so are the requests the camera does not support
- No flapping: a setting the camera refuses for a moment (privacy mode, busy camera) keeps its last value for up to 10 minutes instead of turning its entity unavailable
- Snapshots shared: one FFmpeg at a time per camera, a still reused for 5 s, and stills taken from the running stream when there is one, so dashboards don't use up the camera's two RTSP sessions
- Dead event connections detected: a pull without any answer 15 s past its timeout restarts the subscription; an empty answer arriving at once is not pulled again for a second
- Changes shown at once, then confirmed by a refresh 2 s later; several changes in a row (sliders, moves) end in one refresh
- No login storms: after a failed login the integration waits (10 s, 30 s… up to 5 min) instead of hitting a camera that locks itself after too many attempts; a wrong password opens the re-authentication flow
- Entities follow what the camera answers: an unsupported setting gets no entity
- Switches: privacy mode, status LED, notifications, rich notifications, audio recording, microphone mute, noise cancelling, lens distortion correction, flip, auto track, smart track per target, privacy zones, recording to SD card, loop recording, line crossing detection, scheduled reboot, automatic firmware upgrade, diagnose mode, and which detections trigger the alarm (disabled by default)
- Selects: motion, person, vehicle, pet, package, baby crying, barking, meowing, glass break and tamper detection (off / low / normal / high), night vision, day/night mode, light frequency, presets, stream quality
- Numbers: speaker and microphone volume, motion sensitivity, spotlight intensity, step of the move buttons
- Buttons: reboot, move up / down / left / right, calibrate, format SD card (disabled by default)
- Siren (the camera's alarm), spotlight and floodlight lights, firmware update entity
- Sensors: Wi-Fi signal, SD card status, free space and size; Wi-Fi network, connection type and IP address (disabled by default)
- Actions to pan/tilt by an angle and to go to, save or delete a preset
- DHCP discovery, and a camera that changes address is followed by its MAC address
- Reconfigure (address, accounts) without removing the camera
- Diagnostics with personal data redacted
- English, French, German, Spanish, Italian, Dutch, Portuguese (European and Brazilian), Polish, Serbian (Cyrillic and Latin), Romanian, Danish, Swedish, Bulgarian, Russian, Ukrainian, Hungarian, Slovak, Czech, Slovenian, Latvian and Greek
Prerequisites
Prerequisites
- Home Assistant 2025.8 or newer
- In the Tapo app: Third-Party Compatibility turned on (under Me), and a camera account created in the camera's Advanced Settings → Camera Account
- From Home Assistant, the camera must be reachable on ports 443 (settings), 554 (RTSP stream) and 2020 (ONVIF events). These are local ports: never forward them on your router
- Use only one integration per camera: the camera accepts few simultaneous connections
Configuration
Configuration
Settings → Devices & services → Add integration → Tapo Cameras, or accept the discovered camera.
| Field | Description |
|---|---|
| Host | IP address of the camera (give it a fixed address in your router) |
| Tapo account password | Password of the TP-Link account the camera is added to: used for the settings |
| Camera account username / password | The camera account created in the app: used for the stream and the detection events |
| Control port | 443 for cameras and doorbells |
Options#
| Option | Default | Description |
|---|---|---|
| Refresh interval | 30 s | How often the settings are read. Detections are pushed by the camera, whatever this interval |
| Detection events | on | Detection sensors from the camera's ONVIF events |
| Detection hold time | 30 s | A detection stays on this long after the camera ends it (cameras end and start again every few seconds while something moves); 0 to follow the camera |
| Detection safety timeout | 60 s | A detection without news for this long is switched off; 0 to wait for the camera |
| RTSP transport | TCP | TCP, UDP, UDP multicast or HTTP |
| Extra FFmpeg arguments | — | Added to the FFmpeg command that takes snapshots |
Entities
Entities
The camera entity is named after the camera; the others are named after what they control (switch.living_room_privacy_mode, binary_sensor.living_room_person…).
| Entity | Notes |
|---|---|
camera.<name> |
Stream in the quality of select.<name>_stream_quality; turn on/off = privacy mode; in privacy mode, no live stream and a crossed-out camera as picture; attributes stream_quality, privacy_mode |
binary_sensor.<name>_motion, _person, _vehicle… |
From the event subscription; unavailable only while it is down; attribute last_change |
binary_sensor.<name>_detection_events |
Subscription state; attributes last_error, messages |
select.<name>_<type>_detection |
off, low, normal, high |
select.<name>_preset |
Moves to the chosen preset; the state stays unknown (the camera has no current preset) |
number.<name>_move_step |
Degrees of the move buttons (15 by default), kept by Home Assistant |
Event tapo_cameras_event#
Fired when a detection starts:
trigger:
- trigger: event
event_type: tapo_cameras_event
event_data:
type: person
action:
- action: notify.mobile_app_phone
data:
message: "Someone at {{ trigger.event.data.host }}"
Data: device_id, host, type (motion, person, vehicle, pet, line_crossing, tamper, baby_cry, bark, meow, glass_break, package, area_intrusion, or the name the camera gives a new kind of detection).
Actions#
All target a camera entity.
action: tapo_cameras.ptz
target:
entity_id: camera.living_room
data:
pan: 30 # degrees to the right, negative to the left
tilt: -10 # degrees up, negative down
action: tapo_cameras.go_to_preset # also save_preset (field: name) and delete_preset
target:
entity_id: camera.living_room
data:
preset: Door # name or ID
How it works
How it works
- Settings: the camera's local API on port 443, signed in as
adminwith the Tapo account password (the camera's own rule), through thepytapolibrary. Every refresh is one batched request (unused requests left out, slow and unsupported ones every 10 minutes); every call goes through one queue per camera, run in the executor; a request that timed out is not retried at once, as it may still be running - Stream: RTSP on port 554 with the camera account (
stream1= HD,stream2= SD). Snapshots are taken from the running stream when there is one, so they don't use one of the camera's two stream slots - Detections: ONVIF pull point on port 2020 with the camera account, pulled every 20 s at most (the camera answers as soon as something happens), subscription renewed before it expires
Changelog
All 7 versions 1.3.0Nov 11, 2026Latest
Improvements
- New option Detection hold time (30 s by default): a detection stays on this long after the camera ends it, and a new start in the meantime continues it. Tapo cameras end a detection and start it again every one to four seconds while something moves, so the sensors switched off and on as often and every start fired a
tapo_cameras_event; now one movement is one detection and one event. 0 follows the camera as before.
1.2.2Nov 11, 2026
Fixes
- Detection sensors flapping between off and unavailable every 20 s or so: Tapo firmwares cut the connection of a long event pull instead of answering it empty (
Server disconnected), and each cut made a new subscription, the sensors unavailable meanwhile. A cut pull is pulled again on the same subscription (up to 3 times in a row), a pull cut at the end of its wait is shortened (20 s, then 10 s, down to 5 s), and a subscription that worked is replaced at once without the sensors turning unavailable: they only do when no new subscription can be made.
1.2.1Nov 11, 2026
Fixes
- Detection sensors unavailable since 1.1.1: the ONVIF client was built in the executor, where its HTTP sessions find no event loop (
events unavailable (no running event loop)), so the subscription never came up. Only its import runs in the executor now; the client is built in the event loop.
1.2.0Nov 11, 2026
Improvements
- Lighter refreshes: the requests a camera does not support (floodlight, siren, white lamp… depending on the model), refused at a full refresh and never answered before, are left out of the refreshes in between and asked again every 10 minutes. The camera has less to process while it streams. Diagnostics list them under
unsupported.
1.1.1Nov 11, 2026
- Detection events: the ONVIF client is loaded and built outside Home Assistant's event loop, which no longer reports blocking calls (package metadata, WSDL files, certificates) at startup
1.1.0Nov 11, 2026
- Privacy mode: the camera shows a large crossed-out camera instead of a black or frozen picture; the live stream stops and comes back when privacy mode is turned off. The camera entity stays on, with a
privacy_modeattribute - Entity names (and select states) are no longer translated: English in every language, so names and entity IDs don't depend on the language of Home Assistant. Configuration, options, actions and errors stay translated
- Stills taken from the running stream only when the stream integration is loaded; otherwise one shared FFmpeg snapshot