simplisafe-python
simplisafe-python copied to clipboard
SimpliSafe Camera Integration
So the "SimpliCam" as they call it is a combination camera and motion sensor. The camera itself is joined to your local network wifi during setup. I am unsure if the included sensor is also communicating via the local network or their standard communication protocol.
You stated you don't have a camera of your own, so let me know how I can help.
Thanks for offering to help! I need to understand how the SimpliSafe web app communicates with the camera. Here's my suggestion:
- Open Google Chrome.
- Navigate to https://webapp.simplisafe.com and login.
- Open the Chrome Developer Tools (
View >> Developer >> Developer Tools
). - Browse around various camera "operations" – open the camera page, view the camera, stop viewing the camera, go elsewhere, etc.
- When done, go back to the Developer Tools and click the
Export HAR
link (see below). Don't post that here; send it to me in a DM on the HASS Community Forum.
Thanks for the payload, @tbrock47. Here's what I've learned:
Once you log into the web app and access the camera page, a URL like this is request:
https://media.simplisafe.com/v1/<SOME_ID>/flv?x=472&audioEncoding=AAC
I assume that this is the stream URL to access, although when I attempted to access yours, I got Unauthorized
. I'm assuming this is either because (a) the access token is shorted lived or (b) I got the headers wrong.
I'll continue to investigate.
Also, how does the operation of viewing previous motion events work?
Also, how does the operation of viewing previous motion events work?
The motion events aren't logged on the Cameras page. They show up as a line item under the Timeline page as "Camera Motion Detected". The Cameras page is just a tile layout of all your cameras which allows you to click on one to get a live view.
I'm honestly more interested in just getting a live stream over anything else. The reset would be a bonus for a later date IMO.
Got it. But once you get the livestream going, when you add it to Home Assistant, you probably won’t want to have live view always on. (Maybe take a “snapshot” every hour.)
Hey all - been digging into this as well. So the <SOME_ID>
(which is a unique camera ID) mentioned above comes through during the https://api.simplisafe.com/v1/users/<user_id>/subscriptions
GET request. In the JSON response, you'll find your camera ID in subscriptions-><first subscription entry>->location->system->cameras-><first entry>->uuid
. This is then used to point to the link mentioned above; https://media.simplisafe.com/v1/<Camera UUID>/flv?x=472&audioEncoding=AAC
. I'm having trouble with the get_systems
call at the moment, but will try to resolve that soon and then try fetching the uuid through the get_systems
API (might already be coming through but I can't tell unfortunately).
Thanks @ph1ash! Let me know what I can to to assist you.
Out of curiosity, the SimpliSafe camera is available to view on the SimpliSafe app without any subscription, are you looking into a way to be able to view the camera without pulling the subscription settings?
I don't really know what I am doing in python but I was able to stream the flv to a file and watch it with vlc using this code:
import asyncio
from aiohttp import ClientSession
from simplipy import *
async def get_subscriptions(user_id: str, access_token: str, client: ClientSession):
url = 'https://api.simplisafe.com/v1/users/{}/subscriptions'.format(user_id)
print("Url To Send Call To: {}".format(url))
async with client.get(url, headers={'Authorization': 'Bearer {}'.format(access_token)}) as resp:
return await resp.json()
async def get_camera_feed(camera_id: str, access_token: str, client: ClientSession):
url = 'https://media.simplisafe.com/v1/{}/flv?x=472&audioEncoding=AAC'.format(camera_id)
print("Url To Send Call To: {}".format(url))
with open('testing.flv', 'wb') as file:
async with client.get(url, headers={'Authorization': 'Bearer {}'.format(access_token)}) as resp:
async for data in resp.content.iter_chunked(1024):
file.write(data)
async def main() -> None:
"""Create the aiohttp session and run."""
async with ClientSession() as session:
simplisafe = await API.login_via_credentials(
"YOUR_EMAIL_HERE", "YOUR_PASSWORD_HERE", session=session
)
systems = await simplisafe.get_systems();
for s in systems:
system = systems[s]
subscriptionsBody = await get_subscriptions(simplisafe.user_id, simplisafe.access_token, session)
cameraId = subscriptionsBody["subscriptions"][0]["location"]["system"]["cameras"][0]["uuid"]
print("Camera Id: {}".format(cameraId))
print("Subscriptions: {}".format(subscriptionsBody))
await get_camera_feed(cameraId, simplisafe.access_token, session)
asyncio.run(main())
@jaredswarts55 what do you mean "stream"? Did you just press Ctrl+C to stop it when you were done? I'd be interested if there was a way to get past camera recordings too.
@KTibow yeah, the request was open and streaming to the file, increasing its file size, I had to hit Ctrl+C to stop it.
@jaredswarts55 so at the end of it did VLC complain about invalidness, or was it okay since you chunked them into 1024-byte parts?
Hey guys, I work on a node implementation of the SS api for Apple HomeKit nzapponi/homebridge-simplisafe3, we've had camera integration for a while though obviously its different we pipe through ffmpeg to an rstp stream (which is what Apple requires).
Unfortunately it seems theres no way to locally tap into the cameras as you've noted but you guys have the right URL FWIW, theres also an mjpeg stream available at:
https://media.simplisafe.com/v1/{cameraID}/mjpg?x=${width}&fr=1
, and finally wanted to mention if you leave off the audioEncoding=AAC
you will get the h.264 stream with speex audio instead of aac.
Good luck! I will be following along in case I can learn something for my project =)
@KTibow vlc is pretty good about continuously reading from the disk and doing its best to watch the video stream, when I ended it, vlc stopped like the video was over.
@jaredswarts55 here's your code modified a little (so it streams a certain amount):
import asyncio, time
from aiohttp import ClientSession
from simplipy import *
async def get_subscriptions(user_id: str, access_token: str, client: ClientSession):
url = 'https://api.simplisafe.com/v1/users/{}/subscriptions'.format(user_id)
async with client.get(url, headers={'Authorization': 'Bearer {}'.format(access_token)}) as resp:
return await resp.json()
async def get_camera_feed(camera_id: str, access_token: str, client: ClientSession, capture_time: int):
start_time = time.time()
url = 'https://media.simplisafe.com/v1/{}/flv?x=472&audioEncoding=AAC'.format(camera_id)
with open('testing.flv', 'wb') as file:
async with client.get(url, headers={'Authorization': 'Bearer {}'.format(access_token)}) as resp:
async for data in resp.content.iter_chunked(1024):
file.write(data)
if time.time() - capture_time > start_time:
print("Captured "+str(capture_time)+" seconds of video, exiting.")
break
async def main() -> None:
"""Create the aiohttp session and run."""
async with ClientSession() as session:
simplisafe = await API.login_via_credentials(
input("What's your email: "), input("What's your password: "), session=session
)
systems = await simplisafe.get_systems();
for s in systems:
system = systems[s]
subscriptionsBody = await get_subscriptions(simplisafe.user_id, simplisafe.access_token, session)
cameraId = subscriptionsBody["subscriptions"][0]["location"]["system"]["cameras"][0]["uuid"]
await get_camera_feed(cameraId, simplisafe.access_token, session, int(input("How many seconds?")))
asyncio.run(main())
Sorry if I forgot to put await
somewhere, I don't know asyncio
programming that well.
This is pretty darned cool. And just to add some inspiration here, I modified the script that @jaredswarts55 posted to output the text to stderr and the data to stdout. I was able to pipe that to ffmpeg which would then connect to an ffserver to rebroadcast the stream in a meaninful way, and connect that stream into ZoneMinder. This has the potential to give you the ability to stream and store your videos on your own machine, but sadly it still requires you to have a subscription for the camera.
One major drawback, the SimpliSafe API silently stops sending video after 5 minutes. (4:58 to be more exact). There are potential ways around this issue, like having a small buffer to use while you re-setup the connection, but I haven't gotten to playing with that. It does look like there has to be about a 30 second cool-down on the API before making another request. I really hope I am missing something and that limit can be overcome. @nikonratm Have you seen the 5 minute limit?
Anyone want to try to analyze the network traffic when you try to stream for 11 minutes and see how it bypasses the 5 minute limit? (Make sure to keep the device awake.)
OH! This may not be a simplisafe limit! It appears that this could be a timeout on the aiohttp ClientSession object in python... https://docs.aiohttp.org/en/stable/client_reference.html
@uberlinuxguy Looks better if you add ?highlight=timeout
to the end:
https://docs.aiohttp.org/en/stable/client_reference.html?highlight=timeout
Looks like we need to pass it a ClientTimeout somewhere:
https://docs.aiohttp.org/en/stable/client_reference.html?highlight=timeout#aiohttp.ClientTimeout
This should work (I've added in a 24-hour timeout, so just refresh the session every day, just in case it gets stale):
timeout=aiohttp.ClientTimeout(total=60*60*24, connect=None,
sock_connect=None, sock_read=None)
@KTibow The docs say if you set any or all of the timeouts to 0 or none they are disabled. I am currently running that test to see what happens... 4 mins in...
First test with the timeouts set to zero ran until 6:20s. I think my camera may be flaky or something.
I'll keep trying and post my modified code later once I work out the kinks.
Edit: A second test is still running at 12:30s... HOORAY!
@uberlinuxguy awesome work. The feed is definitely kinda temperamental about long-term streaming, we have the same issues but never looked too deeply into it. One thought would be to consider taking X second chunks to save to disk. Eg this snippet gives 10 second mp4s which can easily be joined back together:
ffmpeg -re -headers "Authorization: Bearer xxx=" -i "https://54.82.224.248/v1/xxx/flv?x=640&audioEncoding=AAC" -vcodec copy -pix_fmt yuv420p -r 20 -b:v 132k -bufsize 264k -maxrate 132k -f segment -segment_time 10 -segment_format_options movflags=+faststart -reset_timestamps 1 segment%d.mp4
ffmpeg -re -headers "Authorization: Bearer xxx=" -i "https://54.82.224.248/v1/xxx/flv?x=640&audioEncoding=AAC" -vcodec copy -pix_fmt yuv420p -r 20 -b:v 132k -bufsize 264k -maxrate 132k -f segment -segment_time 10 -segment_format_options movflags=+faststart -reset_timestamps 1 segment%d.mp4
Might be doable with ffmpeg-python... we could concat the two files...
Any updates on this? It sounds doable.
Like @shamoon, I've implemented camera integration in a PHP script at bggardner/simplisafe-proxy. The whole script takes a very minimalist approach, especially during authentication, as I only pass what seems to be necessary, instead of fully mimicking the WebApp. Feel free to steal ideas or ask questions (though it's been a while since I've looked at it).
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
Go away bot, it's not solved. You're just making unnecessary notifications.
@KTibow This is a valid use case of the bot: it's been over 30 days since this issue was last touched. Let's add the help wanted
label.
I had a stream that was "working", but abandoned the idea for a few reasons:
- It required a multi-step process to get it into something useful like motion-eye. (ie, python script to read from simplisafe, ffmpeg to reformat and send to an ffserver for RTSP so motion-eye could read it)
- The stream was flaky and I could not come up with a good way to buffer it as it would go out for >30 seconds. This could have been my wifi, which is highly possible, but it was so unstable it was unusable.
- When the camera was streaming it was not available in the simplisafe app. Attempting to access it would not work and would actually hard reset the camera sometimes.
- Streaming the camera also seemed to turn off simplisafe's motion detection.
If someone is still interested in working on this, I may be able to dig up the python I was using, but it was a small modification of what is already posted here to stream endlessly to stdout which was redirected to ffmpeg
Quick comment: #182 added initial support for this, but the comments here (@jhujasonw's more recently) are still worth consideration before we can consider this fully closed. So, going to leave this open for further thought and effort.