Eufy Camera Integration

yes,yes,yes! I got my doorbell a month ago and was floored that this was not already integrated in HA. I’ve even sat down a handful of times to work through the API, but web auth and APIs are totally foreign to me (systems programmer here). I’m checking this out now!

1 Like

Great! Welcome to the party. :sunglasses: We’re very early in this library’s life, so any feedback on what doesn’t work, confusion, etc. is helpful.

VERY early stages, but I’ve gotten a still image from the cameras into HASS:

The RTSP stream is super flaky, so that’ll be my next area of focus.

4 Likes

I know from testing the RTSP stream only lasts for 3 minutes max. Is that the issue you’re hitting? Or is just just flakey in starting?

Alright, more movement. Will share what I have so far.

First, to answer your question @fuzzymistborn:

I was seeing flakiness in starting, but I have a strategy for that. Your comment re: 3 minutes is interesting (and was unknown to me); will be curious to get your thoughts on this moving forward.


My first effort at a HASS integration attempted to work like this:

  1. Display the most recent screenshot (that is, the most recent recorded activity) in the “base” Lovelace card.
  2. When the user clicks the image, attempt to start the stream (/api/v1/web/equipment/start_stream).
  3. When the user exits the stream UI, stop the stream (/api/v1/web/equipment/stop_stream).

#1 worked fine, but #2 had the flakiness (sometimes the stream would start, sometimes it wouldn’t) and #3 didn’t appear possible.

I had a hunch that the RTSP flakiness was due to doing too much at once. So, my second effort had an altered flow:

  1. #1 remains the same.
  2. The user fires the camera.turn_on service to start the stream.
  3. The user can interact with the camera stream as desired (noting @fuzzymistborn’s commentary re: a possible 3-minute max lifespan).
  4. The user fires the camera.turn_off service to stop the stream.

This method seems to work consistently – the RTSP flakiness is massively reduced.

This method is a tad quirky compared to the other camera platforms (which assume always-on availability), but it makes logical sense to me for a battery-powered camera. I could see users creating input booleans to fire the camera.turn_on and camera.turn_off services and placing them alongside the camera entities.

I’d like feedback from the doorbell users; in that scenario, power is always provided, but again, if the RTSP stream dies after 3 minutes there, too, perhaps the same model works?

1 Like

Nice progress.

Too bad the first approach doesn’t work, that would be ideal. I suspect the 3 minute limit is because battery cams which they just carried over to the doorbell.

With Lovelace being able to add things like input_booleans to an image card, i think the second approach will work fine. Even for doorbells. Don’t want to overtax servers or get this API killed or anything like that.

2 Likes

Thanks, @fuzzymistborn!

I think I’m in a good spot to have other people start testing this. Instructions below.

Install Custom Component

All files are located in this draft HASS PR: https://github.com/home-assistant/home-assistant/pull/28443

Download the entire eufy_security directory and put it under /config/custom_components. Then, in configuration.yaml, add these lines:

eufy_security:
  username: YOUR_EMAIL
  password: YOUR_PASSWORD 

Usage

Upon HASS startup, you should see new camera entities for every camera/doorbell in your account. Reminder that the image shown isn’t live – it’s the last captured activity snapshot.

To view the RTSP stream for any camera:

  1. Go to Developer Tools >> Services.
  2. Select the camera.turn_on service.
  3. Select the camera you wish to view.
  4. Click Call Service.
  5. Back in your main HASS UI, click on the camera to view the stream.
  6. When done, for good measure, return to Developer Tools >> Services and call camera.turn_off on that camera.

Additional Considerations

  • Right now, the camera image (i.e., what’s shown in the UI before you click on a camera) is a snapshot of the last captured activity; this is very easy to retrieve, but it isn’t near-realtime. Is that sufficient? I can look into whether it’s possible to forcibly update that screenshot.
4 Likes

Hopefully will have time to test this weekend.

I believe the app updates the image on a sporadic basis. Don’t know how often that is but it’s not just when the cam is clicked/stream is active. Maybe it’s just the doorbell but i know it goes from day to night without me loading the stream.

2 Likes

I have a doorbell, I’ll give this a try over the weekend. Thanks.

1 Like

Tested this out in a dev instance. Not bad. Getting the stream to work is still touchy, I added an input_boolean to turn the camera on/off so I didn’t have to go into the dev panel all the time and maybe I was toggling too often but the stream stopped working till I stopped and restarted the docker container my instance was running in.

Also, I’m not sure the functionality as of now is ideal. When I click the camera image i feel like it should create a larger still if the camera isn’t running, and if the camera is running then show the stream. Not sure that’s possible but that would be an improvement.

Still need to figure out how they’re calling changes to the schedule/camera settings. Also, would be nice if there was a way to get a notification when motion was detected. All that probably lurks inside the APK.

Final thought: Just had the stream time out on me. Now I can’t get it to restart again. Have to restart the container to get it to work. I get an error that FFmpeg times out, and this:

Error while executing automation automation.eufy_off. Unknown error for call_service at pos 1: 
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/components/automation/__init__.py", line 437, in action
    await script_obj.async_run(variables, context)
  File "/usr/src/homeassistant/homeassistant/helpers/script.py", line 190, in async_run
    await self._handle_action(action, variables, context)
  File "/usr/src/homeassistant/homeassistant/helpers/script.py", line 274, in _handle_action
    await self._actions[_determine_action(action)](action, variables, context)
  File "/usr/src/homeassistant/homeassistant/helpers/script.py", line 357, in _async_call_service
    context=context,
  File "/usr/src/homeassistant/homeassistant/helpers/service.py", line 97, in async_call_from_config
    domain, service_name, service_data, blocking=blocking, context=context
  File "/usr/src/homeassistant/homeassistant/core.py", line 1236, in async_call
    await asyncio.shield(self._execute_service(handler, service_call))
  File "/usr/src/homeassistant/homeassistant/core.py", line 1261, in _execute_service
    await handler.func(service_call)
  File "/usr/src/homeassistant/homeassistant/helpers/entity_component.py", line 213, in handle_service
    self._platforms.values(), func, call, service_name, required_features
  File "/usr/src/homeassistant/homeassistant/helpers/service.py", line 348, in entity_service_call
    future.result()  # pop exception if have
  File "/usr/src/homeassistant/homeassistant/helpers/service.py", line 370, in _handle_service_platform_call
    await getattr(entity, func)(**data)
  File "/config/custom_components/eufy_security/camera.py", line 103, in async_turn_off
    await self._camera.async_stop_stream()
  File "/usr/local/lib/python3.7/site-packages/eufy_security/camera.py", line 81, in async_stop_stream
    "proto": 2,
  File "/usr/local/lib/python3.7/site-packages/eufy_security/api.py", line 93, in request
    _raise_on_error(data)
  File "/usr/local/lib/python3.7/site-packages/eufy_security/api.py", line 115, in _raise_on_error
    raise_error(data)
  File "/usr/local/lib/python3.7/site-packages/eufy_security/errors.py", line 29, in raise_error
    raise cls(data["msg"])
eufy_security.errors.EufySecurityError: Failed to request.
1 Like

This is part of the flakiness I mentioned previously: at random intervals, the stream fails to start. Not sure I can do much about this.

I can do that!

Agreed. I’ve dug around the decompiled APK, but my Java is very rusty and I haven’t found anything obvious…

Got it. Not sure I can do much about the underlying issue, but at a minimum, I can catch and deal with that exception. Will dig around.

2 Likes

Latest updates in the PR: https://github.com/home-assistant/home-assistant/pull/28443

This should show an image when the stream is turned off or the stream is flaky; if the stream is working (and the camera is turned on), it should show the stream.

1 Like

Anyone get a chance to play with the integration further?

My cams are arriving Friday so as soon as I get them set up, I’ll be testing.

1 Like

Awesome I’ve got a doorbell I’ll work on testing ASAP and I’m planning on installing 2 of the spot light cams in the near future

1 Like

I have not, had a busy weekend/few days. Will try to test updated code tonight.

1 Like

Got around to testing. Noticed some bugs:

I tried to turn the stream on, and it wouldn’t work. Got an error that FFMPEG failed to start. Then when I tried to stop, error of “Unable to stop stream (Front Door): Failed to request.” And it no longer would pull up an image, would just be a blank window.

Rebooted HASS and it worked like normal. There’s some slowness between switching from the stream back to the image, but it works well enough. I think it’s ready to add to HASS.

1 Like

Thanks for the feedback. Unfortunately, FFMpeg is faltering because the streams from Eufy aren’t the most solid.

Wondering if it’s worth the hassle then.

Also wondering how to get the latest “background” update image. App definitely shows a more up to date image than just last event. Guess we can add that to the list of things to look for in the app.

As I mentioned previously, my static image seems to relate to the last motion detected – I can fairly consistently get my HASS image to show that, at least.

What would you think about me releasing, but putting a caveat in the docs that the stream isn’t super reliable?