-
Notifications
You must be signed in to change notification settings - Fork 34
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SimpliSafe Camera Integration #85
Comments
Thanks for offering to help! I need to understand how the SimpliSafe web app communicates with the camera. Here's my suggestion:
|
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:
I assume that this is the stream URL to access, although when I attempted to access yours, I got I'll continue to investigate. |
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 |
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: 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 |
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... |
@uberlinuxguy Looks better if you add 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:
|
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. |
This comment has been minimized.
This comment has been minimized.
I had a stream that was "working", but abandoned the idea for a few reasons:
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. |
I have a simplisafe doorbell camera. Is there anything I can do to assist with getting the cameras (especially the doorbell camera) better integrated? |
@veedubb65 Read all the above and see where you can help. |
Hey @bachya, thanks for your help getting my "refuse to pay for any level of service" simplisafe setup working, and as we discussed, I've got the doorbell with camera that I'd be more than happy to hack on. |
Hi everyone, wondering if there's been any progress on accessing the Simplisafe Cam stream from HA. I'd love to be able to do that. Thanks @bachya for all the hard work you've put into this awesome integration, definitely one of the most important (if not the most) integrations I have. |
For the outdoor camera, in case you haven't seen the timeline response, looks like this: GET https://api.simplisafe.com/v1/subscriptions/6720379/events?numEvents=50 {
"numEvents": 50,
"lastEventTimestamp": 1697547602,
"events": [
{
"eventId": 35952684503,
"eventTimestamp": 1697598147,
"eventCid": 1170,
"zoneCid": "1",
"sensorType": 17,
"sensorSerial": "f11b6abd",
"account": "00499FF8",
"userId": 5913258,
"sid": 6720379,
"info": "Camera Detected Motion",
"pinName": "",
"sensorName": "Back Yard",
"messageSubject": "Camera Detected Motion",
"messageBody": "Camera Detected Motion on 10/17/2023 at 8:02 PM",
"eventType": "activityCam",
"timezone": 3,
"locationOffset": -420,
"videoStartedBy": "6172311af9da430ab2e11c59f11b6abd",
"video": {
"6172311af9da430ab2e11c59f11b6abd": {
"clipId": 24021857466,
"preroll": 1,
"postroll": 12,
"cameraName": "Back Yard",
"cameraModel": "SSOBCM4",
"aspectRatio": "16:9",
"duration": 13,
"recordingType": "KVS",
"account": "611485993050",
"region": "us-east-1",
"_links": {
"_self": {
"href": "https://chronicle.us-east-1.prd.cam.simplisafe.com/v1/recordings/24021857466",
"method": "GET"
},
"preview/mjpg": {
"href": "https://remix.us-east-1.prd.cam.simplisafe.com/v1/preview/6172311af9da430ab2e11c59f11b6abd/6720379/time/1697598146/1697598159?account=611485993050®ion=us-east-1{&fps,width}",
"method": "GET",
"templated": true
},
"snapshot/mjpg": {
"href": "https://remix.us-east-1.prd.cam.simplisafe.com/v1/preview/6172311af9da430ab2e11c59f11b6abd/6720379/time/1697598146/1697598150?account=611485993050®ion=us-east-1{&fps,width}",
"method": "GET",
"templated": true
},
"snapshot/jpg": {
"href": "https://remix.us-east-1.prd.cam.simplisafe.com/v1/snapshot/6172311af9da430ab2e11c59f11b6abd/6720379/time/1697598146?account=611485993050®ion=us-east-1{&width}",
"Method": "GET",
"templated": true
},
"download/mp4": {
"href": "https://remix.us-east-1.prd.cam.simplisafe.com/v1/download/6172311af9da430ab2e11c59f11b6abd/6720379/time/1697598146/1697598159?account=611485993050®ion=us-east-1",
"method": "GET"
},
"share": {
"href": "https://remix.us-east-1.prd.cam.simplisafe.com/v2/share/6172311af9da430ab2e11c59f11b6abd/6720379/time/1697598146/1697598159?account=611485993050®ion=us-east-1",
"method": "POST"
},
"playback/dash": {
"href": "https://mediator.prd.cam.simplisafe.com/v1/recording/6172311af9da430ab2e11c59f11b6abd/6720379/time/1697598146/1697598159/dash?account=611485993050®ion=us-east-1",
"method": "GET"
},
"playback/hls": {
"href": "https://mediator.prd.cam.simplisafe.com/v1/recording/6172311af9da430ab2e11c59f11b6abd/6720379/time/1697598146/1697598159/hls?account=611485993050®ion=us-east-1",
"method": "GET"
}
}
}
}
},
... I used curl on the snapshot/jpg url (with the Authorization: Bearer token) and got a good snap from the camera. |
how do we look at the feed? can we call this API from, lets say javascript or react? I took this URL into a browser and it didnt work... |
The code should be pretty easily portable if you consider things like sending authorization |
I'd love to see this working so I could view the SimpliSafe cameras in Home Assistant (running HAOS on a Raspberry Pi 4) like I do with Hikvision currently. I've got SimpliSafe on the Pro Premium plan (UK) in case anyone needs help testing this to get it working. I'm no coder sadly. |
For home assistant support, its not in this repo that changes would need to be made. https://github.com/home-assistant/core.git, under homeassistant/components/simplisafe/, the code in HA that uses this repo. |
Locally, I have made modifications to home-assistant/core/homeassistant/components/simplisafe for the OUTDOOR_CAMERA as described below, and I wonder if this is a good approach. I treat the OUTDOOR_CAMERA as a binary_sensor (motion). This new sensor listens for EVENT_CAMERA_MOTION_DETECTED coming from the Simplisafe websocket. When this event occurs it fetches the "event list" (or timeline) from Simplisafe server and looks for events targeted at the right device serial number, occurring at the same timestamp as the websocket event. These raw SS timeline events contain the media urls for the motion event (see my comment above for the format). I download the snapshot/jpg and download/mp4 files and store them under /config/www/simplisafe//[latest_snapshot.jpg, latest_clip.mp4] and ./clips/YYYYMMDDHHmmss.mp4. I use the HACS gallery card to browse the clips. I use the latest_snaphot.jpg in notification automations. It does what I want for the OUTDOOR_CAMERA. Its not a camera, in that there is no way to stream the current video ... there does not seem to be a good way of doing this for these camera types. |
It may not be a full-fledged video feed, but it is much better than what we currently have (nothing). I'm not sure what the HA maintainers think, but I would be excited to have this level of camera support in the SimpliSafe integration. Does it work for the doorbell too, or just the outdoor camera? |
Can you share your implementation? Ive tried many ways but cant get this one to work for me? |
I'll go through the developer checklist and do a pull request, maybe today. |
@mginz83 So you know how to run an integration with changes locally in a running HA? If you do, you'll find my changes in https://github.com/peebles/core/tree/simplisafe-outdoor-motion-capture/homeassistant/components/simplisafe, specifically in I have submitted a PR, but it might take a while to be approved and released. |
@peebles can you link to the PR here? It would be nice to see a record of all the various attempts in this issue. |
I modified @jaredswarts55 solution to use an Auth Code/Code Verifier: 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.async_from_auth(
"BFHQc62Rfb6HdUhbcATWVNffUlNxgWfnSEWank1IldUyP",
"GWvuu5a22As6EVvGuoLMFkFZqangk2GSqkYSeDRhl52aTB96yg",
session=session,
)
systems = await simplisafe.async_get_systems()
for s in systems:
system = systems[s]
subscriptionsBody = await get_subscriptions(simplisafe.user_id, simplisafe.access_token, session)
#print("Camera: " + dir(subscriptionsBody["subscriptions"][0]["location"]["system"]["cameras"][0]))
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()) But it streamed to a flv file with 0 bytes. Any idea why? |
Perhaps the request failed. Check the status of |
Just wanted to check if this now supports simplicam on free subscription. I could not access the camera from my end |
Hello, it might be tangent to the rest of the discussion but I thought it's better to ask here: is it possible to have some basic camera integration without the streams per-se? e.g. ability to have motion detection and motion notifications as binary sensors, sensitivity as entity etc. It would be useful to be able to create automations that enable/disable features, even if we can't view the streams on HA. |
@peebles Did you resubmit the PR? @athanasio That's already possible via HA event triggers (although probably will take a little debugging to figure out the payload that the websocket will send). But, if cameras are implemented, it probably would make more sense to implement it internally as binary sensors on a camera device. |
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.
The text was updated successfully, but these errors were encountered: