Synology Surveillance Camera Motion MQTT Gifs

Inspired by this post, I have developed a python script (and dockerized it) to poll motion detection events from Synology attached cameras.
When a new event is detected, the related video is downloaded, a gif is generated and sent to an MQTT topic.

Check out the README file on the github page.

Hope someone will find it useful.

Buy Me A Coffee

3 Likes

Are the Synology cameras working for you? I thought they stopped working with the last surveillance station update (API version 2.8).

based on a quick look of the python script doesn’t look like the livestream API endpoint is being used so this functionality should be fine for users that have updated. Although your question is still valid.

@fabtesta this is super cool, going to check it out. Does this work for people who are recording 24/7? or only those recording when motion is detected?

@fabtesta I am getting this error.

Traceback (most recent call last):
  File "/synology_surveillance_motion_mqtt_gifs.py", line 220, in <module>
    main()
  File "/synology_surveillance_motion_mqtt_gifs.py", line 195, in main
    sid = syno_login(config["synology_base_api_url"], config["synology_user"], config["synology_password"])
  File "/synology_surveillance_motion_mqtt_gifs.py", line 41, in syno_login
    login_data = json.loads(login_response.content.decode('utf-8'))
  File "/usr/local/lib/python3.7/json/__init__.py", line 348, in loads
    return _default_decoder.decode(s)
  File "/usr/local/lib/python3.7/json/decoder.py", line 337, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "/usr/local/lib/python3.7/json/decoder.py", line 355, in raw_decode
    raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

Hi @arsaboo, never stopped working for me.
I’m currently on HA 0.87.1, Syno DSM DSM 6.2.1-23824 Update 4, Surveillance Station 8.2.3-5828.

Hi @w1ll1am23, should work because the motion event is stored to support the timeline markers, so even if you are continuously recording the Event API called for event type “motion” should return the latest result for cameraId.
The problem is that the script does not read from event data the timestamps where motion occurred, so the skip_n_secs and max_length parameters are not really useful to create a gif that shows the actual motion that triggered the event itself.

It seems that the synology auth call gets a non-json response.
Does the “synology_user” configured in DSM has access to surveillance station?

Here is my configuration for “hass” user.

I am using the same user I use for the dacam app. I’ll try to add a new user later today

No luck. Created a new user and made sure those permissions were set correctly. I still get the same error.

So my logs show the request going to /?api=SYNO.API.Auth&method=Login&version=2&session=SurveillanceStation&format=sid&account=hass&passwd=password
but the API doc I see show it should be
/webapi/auth.cgi?api=SYNO.API.Auth&method=Login&version=2&session=SurveillanceStation&format=sid&account=hass&passwd=password

I tested the second one in postman and I get a JSON response of
{"data":{"sid":"XXXXXXX"},"success":true}

I you have not, I suggest to run the script with the docker image or in a virtualenv with the latest 3.7 python available.

I am using the docker image.

I suggest to “curl” the same login API from inside the docker container to see which is the response body.

I think I tried opening the docker console and it wouldn’t work. I did use postman on the original URL and got a 404 basically. I can post the exact content in a couple hours.

Full response calling via postman

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=11" />
        <meta name="msapplication-TileImage" content="resources/images/icon_tile.png?v=4398" />
        <meta name="application-name" content="diskstation&nbsp;-&nbsp;Synology&nbsp;DiskStation" />
        <meta name="msapplication-TileColor" content="#246BB3"/>
        <meta name="description" content="DiskStation provides a full-featured network attached storage (NAS) solution to help you manage, backup and share data among Windows, Mac and Linux easily." />
        <meta name="keywords" content="Multitasking,Web Application,Personal Cloud" />
        <link rel="apple-touch-icon" href="webman/resources/images/icon_dsm_96.png?v=4398" />
        <link rel="mask-icon" href="webman/safari_pin_icon.svg" color="#0086E5" />
        <link rel="shortcut icon" href="webman/favicon.ico?v=4399" />
        <link rel="shortcut icon" href="webman/resources/images/icon_dsm_96.png?v=4398" sizes="96x96"/>
        <link rel="shortcut icon" href="webman/resources/images/icon_dsm_64.png?v=4398" sizes="64x64"/>
        <link rel="shortcut icon" href="webman/resources/images/icon_dsm_48.png?v=4398" sizes="48x48"/>
        <link rel="shortcut icon" href="webman/resources/images/icon_dsm_32.png?v=4398" sizes="32x32"/>
        <link rel="shortcut icon" href="webman/resources/images/icon_dsm_16.png?v=4398" sizes="16x16"/>
        <title>diskstation&nbsp;-&nbsp;Synology&nbsp;DiskStation</title>
        <link rel="stylesheet" type="text/css" href="scripts/ext-3/resources/css/ext-all.css?v=23824-s4" />
        <link rel="stylesheet" type="text/css" href="scripts/ext-3/resources/css/xtheme-gray.css?v=23824-s4" />
        <link rel="stylesheet" type="text/css" href="scripts/ext-3/ux/ux-all.css?v=23824-s4" />
        <link rel="stylesheet" type="text/css" href="synoSDSjslib/sds.css?v=23824-s4" />
        <link rel="stylesheet" type="text/css" href="webman/resources/css/desktop.css?v=23824-s4" />
        <style type="text/css">
@import url("webman/modules/WelcomeApp/style.css?v=1536310508");
@import url("webman/modules/VideoPlayer2/style.css?v=1536310503");
@import url("webman/modules/AdminCenter/style.css?v=1536310551");
@import url("webman/modules/ConfigBackup/style.css?v=1536310505");
@import url("webman/modules/PkgManApp/style.css?v=1536310506");
@import url("webman/modules/DSMNotify/style.css?v=1536310508");
@import url("webman/modules/FileTaskMonitor/style.css?v=1532537278");
@import url("webman/modules/AviaryEditor/style.css?v=1536310506");
@import url("webman/modules/EzInternet/style.css?v=1536310507");
@import url("webman/modules/Utils/style.css?v=1536310505");
@import url("webman/modules/SecurityScan/style.css?v=1536310539");
@import url("webman/modules/BandwidthControl/style.css?v=1536310330");
@import url("webman/modules/StorageManager/style.css?v=1536310506");
@import url("webman/modules/ClipBoardJS/style.css?v=1536310505");
@import url("webman/modules/DiskMessageHandler/style.css?v=1536310508");
@import url("webman/modules/iSCSI/style.css?v=1536310330");
@import url("webman/modules/HelpBrowser/style.css?v=1536310507");
@import url("webman/modules/PollingTask/style.css?v=1536310509");
@import url("webman/modules/PersonalSettings/style.css?v=1536310505");
@import url("webman/modules/SystemInfoApp/style.css?v=1536310507");
@import url("webman/modules/WelcomeTip/style.css?v=1536310510");
@import url("webman/modules/TaskSchedulerWidget/style.css?v=1536310509");
@import url("webman/modules/SupportForm/style.css?v=1536310507");
@import url("webman/modules/MyDSCenter/style.css?v=1536310508");
@import url("webman/modules/TaskSchedulerUtils/style.css?v=1536310507");
@import url("webman/modules/DataDrivenDocuments/style.css?v=1536310506");
@import url("webman/modules/Share/style.css?v=1536310507");
@import url("webman/modules/HotkeyManager/style.css?v=1536310506");
@import url("webman/modules/AudioPlayer/style.css?v=1536310505");
@import url("webman/modules/ThumbConvertProgress/style.css?v=1536310539");
</style>
        <style type="text/css">
@import url("webman/modules/C3/style.css?v=1536310506");
@import url("webman/modules/LogCenter/style.css?v=1536310491");
@import url("webman/modules/PhotoViewer/style.css?v=1536310507");
@import url("webman/modules/ResourceMonitor/style.css?v=1536310507");
@import url("webman/modules/Widgets/style.css?v=1536310509");
@import url("webman/modules/FileBrowser/style.css?v=1532537283");
@import url("webman/modules/ExternalDevices/style.css?v=1536310507");
</style>
        <link rel="stylesheet" type="text/css" href="webman/3rdparty/OAuthService/style.css?v=1523844595" />
        <link rel="stylesheet" type="text/css" href="webman/3rdparty/VideoStation/style.css?v=1532677838" />
        <link rel="stylesheet" type="text/css" href="webman/3rdparty/SynoFinder/style.css?v=1532426188" />
        <link rel="stylesheet" type="text/css" href="webman/3rdparty/LogCenter/style.css?v=1532315190" />
    </head>
    <body role="application">
        <div id="sds-wallpaper"></div>
        <!-- Don't contain any text node to avoid IE insertBefore bug -->
        <div id="sds-login-dialog-form" style="position: absolute; top: -10000px; left: -10000px;">
            <form id="login-form" class="x-plain-body" method="POST" action="webman/login.cgi" target="login_iframe">
                <input type="text" class="x-form-text" id="login_username" name="username" maxlength="256" />
                <input type="password" class="x-form-text" id="login_passwd" name="passwd" maxlength="256" autocomplete="off" />
                <input class="x-form-text" type="text" id="login_otp" name="OTPcode" maxlength="8" autocomplete="off" />
                <input type="submit" id="login_submit" style="position: absolute; top: -10000px; left: -10000px;" tabindex="-1" />
            </form>
            <iframe id="login_iframe" name="login_iframe" width="0" height="0" frameborder="0" style="display: none;"></iframe>
        </div>
        <div id="sds-apply-preview-form" style="position: absolute; top: -10000px; left: -10000px;">
            <form id="preview-form" class="x-plain-body" method="POST" action="webman/modules/ControlPanel/modules/dsm.cgi" target="preview_iframe">
                <input type="submit" id="preview_submit" style="position: absolute; top: -10000px; left: -10000px;" tabindex="-1" />
            </form>
            <iframe id="preview_iframe" name="preview_iframe" width="0" height="0" frameborder="0" style="display: none;"></iframe>
        </div>
        <script type="text/javascript" src="webapi/entry.cgi?api=SYNO.Core.Desktop.Defs&version=1&method=getjs&v=23824-s4"></script>
        <script type="text/javascript" src="webapi/entry.cgi?api=SYNO.Core.Desktop.JSUIString&version=1&method=getjs&lang=enu&v=23824-s4"></script>
        <script type="text/javascript" src="webapi/entry.cgi?api=SYNO.Core.Desktop.UIString&version=1&method=getjs&lang=enu&v=23824-s4"></script>
        <script type="text/javascript" src="scripts/prototype-1.7.2/prototype.js?v=23824-s4"></script>
        <script type="text/javascript" src="scripts/ext-3/adapter/ext/ext-base.js?v=23824-s4"></script>
        <script type="text/javascript" src="scripts/ext-3/ext-all.js?v=23824-s4"></script>
        <script type="text/javascript" src="scripts/ext-3/ux/ux-all.js?v=23824-s4"></script>
        <script type="text/javascript" src="scripts/scrollbar/flexcroll.js?v=23824-s4"></script>
        <script type="text/javascript" src="synoSDSjslib/sds.js?v=23824-s4"></script>
        <script type="text/javascript" src="webman/desktop.js?v=23824-s4"></script>
        <script type="text/javascript" src="webapi/entry.cgi?api=SYNO.Core.Desktop.SessionData&version=1&method=getjs&SynoToken=--------&v=23824-s4"></script>
        <script type="text/javascript" src="webman/security.cgi"></script>
        <div class="pre-load-x-window-br"></div>
    </body>
    <noscript>
        <div class='syno-no-script'>
            <div class='title align-center'>This page can't be displayed</div>
            <div class='desc align-center'>Please allow your browser to run JavaScript.</div>
            <div class='icon align-center'></div>
        </div>
    </noscript>
</html>

1: Step into container shell: docker exec -it yourcontainername /bin/sh
2: Wget the login API (replace your config values): wget -qO- --no-check-certificate “https://yousynoip:5001/webapi/auth.cgi?api=SYNO.API.Auth&method=Login&version=2&session=SurveillanceStation&form
at=sid&account=dummy&passwd=fake”

With a non working account you should got “{“error”:{“code”:400},“success”:false}”.
If you still got an html response, please send me your DSM version number.

That URL works fine I get a success response. The script in the container however isn’t using that URL it is using ’ /?api=SYNO.API.Auth&method=Loginnot/webapi/auth.cgi?api=SYNO.API.Auth&method=Login`

Sorry but the script always has pointed to “{}/webapi/auth.cgi?api=SYNO.API”.

Did you modify the script inside the container?

Interesting, that’s not what my logs show. I haven’t modified anything. I can send you my logs when I guess home later.

This is what my logs show. Notice it isn’t using webapi and it is also using port 5000 not 5001

[2019-02-20 01:17:43] [DEBUG] (MainThread) Starting new HTTP connection (1): 192.168.5.4:5000
[2019-02-20 01:17:45] [DEBUG] (MainThread) http://192.168.x.x:5000 "GET /?api=SYNO.API.Auth&method=Login&version=2&session=SurveillanceStation&format=sid&account=hass&passwd=PASSWORD HTTP/1.1" 200 None
[2019-02-20 01:17:45] [INFO] (MainThread) login_response status_code 200
Traceback (most recent call last):
  File "/synology_surveillance_motion_mqtt_gifs.py", line 220, in <module>
    main()
  File "/synology_surveillance_motion_mqtt_gifs.py", line 195, in main
    sid = syno_login(config["synology_base_api_url"], config["synology_user"], config["synology_password"])
  File "/synology_surveillance_motion_mqtt_gifs.py", line 41, in syno_login
    login_data = json.loads(login_response.content.decode('utf-8'))
  File "/usr/local/lib/python3.7/json/__init__.py", line 348, in loads
    return _default_decoder.decode(s)
  File "/usr/local/lib/python3.7/json/decoder.py", line 337, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "/usr/local/lib/python3.7/json/decoder.py", line 355, in raw_decode
    raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
[2019-02-20 01:17:47] [INFO] (MainThread) Starting
[2019-02-20 01:17:47] [INFO] (MainThread) Parsing /config/config.json

Port 5001 for me is the https binding.
Maybe you are getting a redirect.

Off-topic, but the Livestream will be fixed, developer is aware and new stream path is coming, so we use the Synology platform again