From 45fbed654adbf29bb106eb6bec57c564ce9587d5 Mon Sep 17 00:00:00 2001 From: Thomas Fransolet Date: Sat, 8 Feb 2020 00:00:08 +0100 Subject: [PATCH] ArloService WIP (understood the logic but still have to generate the transId.. The only missing info) + commit of arlo.py (great ! but have to look if disconnection is handled) + Arlo\arlo-motiondetect.py integrate connection to mqtt server + update in allInOne (user & pswd hassio) --- MyCore/Services/ArloService.cs | 77 +- RPI Code/Arlo/FUNDING.yml | 9 + RPI Code/Arlo/ISSUE_TEMPLATE.md | 43 + RPI Code/Arlo/LICENSE | 201 ++ RPI Code/Arlo/Makefile | 48 + RPI Code/Arlo/README.md | 134 ++ RPI Code/Arlo/__pycache__/arlo.cpython-37.pyc | Bin 0 -> 79431 bytes .../__pycache__/eventstream.cpython-37.pyc | Bin 0 -> 2876 bytes .../Arlo/__pycache__/request.cpython-37.pyc | Bin 0 -> 1544 bytes RPI Code/Arlo/_config.yml | 1 + RPI Code/Arlo/arlo-motiondetect.py | 40 + RPI Code/Arlo/arlo-snapshot.py | 44 + RPI Code/Arlo/arlo.py | 1533 +++++++++++++++ RPI Code/Arlo/dev/html2text.py | 914 +++++++++ RPI Code/Arlo/dev/rev.py | 23 + RPI Code/Arlo/docs/README.md | 1160 +++++++++++ RPI Code/Arlo/docs/arlo.html | 1690 +++++++++++++++++ RPI Code/Arlo/eventstream.py | 111 ++ .../Arlo/examples/arlo-adjustbrightness.py | 36 + RPI Code/Arlo/examples/arlo-cvrdownload.py | 88 + .../Arlo/examples/arlo-download-bycamera.py | 75 + RPI Code/Arlo/examples/arlo-download.py | 59 + .../Arlo/examples/arlo-fullscreensnapshot.py | 31 + RPI Code/Arlo/examples/arlo-motiondetect.py | 32 + .../Arlo/examples/arlo-motiondetect.py.save | 31 + RPI Code/Arlo/examples/arlo-setmode.py | 29 + RPI Code/Arlo/examples/arlo-snapshot.py | 44 + RPI Code/Arlo/examples/arlo-streamingvideo.py | 39 + RPI Code/Arlo/examples/arlo-togglecamera.py | 26 + .../Arlo/examples/arlobaby-audiocontrol.py | 39 + .../examples/arlobaby-nightlightcontrol.py | 34 + .../Arlo/examples/arlobaby-tempcontrol.py | 36 + RPI Code/Arlo/logo.png | Bin 0 -> 10292 bytes RPI Code/Arlo/request.py | 55 + RPI Code/Arlo/requirements.txt | 15 + RPI Code/Arlo/setup.cfg | 5 + RPI Code/Arlo/setup.py | 44 + RPI Code/Arlo/snapshot.jpg | Bin 0 -> 205856 bytes RPI Code/Arlo/tests/__init__.py | 0 RPI Code/Arlo/tests/context.py | 7 + RPI Code/SmartGarden/allInOne.py | 10 +- 41 files changed, 6755 insertions(+), 8 deletions(-) create mode 100644 RPI Code/Arlo/FUNDING.yml create mode 100644 RPI Code/Arlo/ISSUE_TEMPLATE.md create mode 100644 RPI Code/Arlo/LICENSE create mode 100644 RPI Code/Arlo/Makefile create mode 100644 RPI Code/Arlo/README.md create mode 100644 RPI Code/Arlo/__pycache__/arlo.cpython-37.pyc create mode 100644 RPI Code/Arlo/__pycache__/eventstream.cpython-37.pyc create mode 100644 RPI Code/Arlo/__pycache__/request.cpython-37.pyc create mode 100644 RPI Code/Arlo/_config.yml create mode 100644 RPI Code/Arlo/arlo-motiondetect.py create mode 100644 RPI Code/Arlo/arlo-snapshot.py create mode 100644 RPI Code/Arlo/arlo.py create mode 100644 RPI Code/Arlo/dev/html2text.py create mode 100644 RPI Code/Arlo/dev/rev.py create mode 100644 RPI Code/Arlo/docs/README.md create mode 100644 RPI Code/Arlo/docs/arlo.html create mode 100644 RPI Code/Arlo/eventstream.py create mode 100644 RPI Code/Arlo/examples/arlo-adjustbrightness.py create mode 100644 RPI Code/Arlo/examples/arlo-cvrdownload.py create mode 100644 RPI Code/Arlo/examples/arlo-download-bycamera.py create mode 100644 RPI Code/Arlo/examples/arlo-download.py create mode 100644 RPI Code/Arlo/examples/arlo-fullscreensnapshot.py create mode 100644 RPI Code/Arlo/examples/arlo-motiondetect.py create mode 100644 RPI Code/Arlo/examples/arlo-motiondetect.py.save create mode 100644 RPI Code/Arlo/examples/arlo-setmode.py create mode 100644 RPI Code/Arlo/examples/arlo-snapshot.py create mode 100644 RPI Code/Arlo/examples/arlo-streamingvideo.py create mode 100644 RPI Code/Arlo/examples/arlo-togglecamera.py create mode 100644 RPI Code/Arlo/examples/arlobaby-audiocontrol.py create mode 100644 RPI Code/Arlo/examples/arlobaby-nightlightcontrol.py create mode 100644 RPI Code/Arlo/examples/arlobaby-tempcontrol.py create mode 100644 RPI Code/Arlo/logo.png create mode 100644 RPI Code/Arlo/request.py create mode 100644 RPI Code/Arlo/requirements.txt create mode 100644 RPI Code/Arlo/setup.cfg create mode 100644 RPI Code/Arlo/setup.py create mode 100644 RPI Code/Arlo/snapshot.jpg create mode 100644 RPI Code/Arlo/tests/__init__.py create mode 100644 RPI Code/Arlo/tests/context.py diff --git a/MyCore/Services/ArloService.cs b/MyCore/Services/ArloService.cs index 887edf6..b40d0b7 100644 --- a/MyCore/Services/ArloService.cs +++ b/MyCore/Services/ArloService.cs @@ -70,6 +70,30 @@ namespace MyCore.Services public long dateCreated; } + // Motion Detection Test - {"from":"XXX-XXXXXXX_web","to":"XXXXXXXXXXXXX","action":"set","resource":"cameras/XXXXXXXXXXXXX","transId":"web!XXXXXXXX.XXXXXXXXXXXXXXXXXXXX","publishResponse":true,"properties":{"motionSetupModeEnabled":true,"motionSetupModeSensitivity":80}} + + public class MotionDetection + { + public string from; + public string to; + public string action; + public string resource; + public string transId; + public bool publishResponse = true; + public PropertiesRequestMotionSubscription properties; + } + + public class PropertiesRequest + { + public bool motionSetupModeEnabled; + public int motionSetupModeSensitivity; + } + + public class PropertiesRequestMotionSubscription + { + public string[] devices; + } + public enum RequestType { Get, @@ -194,9 +218,56 @@ namespace MyCore.Services var evt = new EventSourceReader(new Uri($"{_clientSubscribeUrl}?token={resultToken.token}")).Start(); evt.MessageReceived += - (object sender, EventSourceMessageEventArgs e) - => - Console.WriteLine($"{e.Event} : {e.Message}"); + async (object sender, EventSourceMessageEventArgs e) + => + { + using (var client = new HttpClient()) + { + ArloDevice baseStation = allArloDevices.Where(d => d.deviceType == "basestation").FirstOrDefault(); + ArloDevice camera = allArloDevices.Where(d => d.deviceType == "camera").FirstOrDefault(); + + Console.WriteLine($"{e.Event} : {e.Message}"); + + // ask for motion event test + //Motion Detection Test - { "from":"XXX-XXXXXXX_web","to":"XXXXXXXXXXXXX","action":"set","resource":"cameras/XXXXXXXXXXXXX","transId":"web!XXXXXXXX.XXXXXXXXXXXXXXXXXXXX","publishResponse":true,"properties":{ "motionSetupModeEnabled":true,"motionSetupModeSensitivity":80} } + /*MotionDetection motionDetection = new MotionDetection(); + motionDetection.from = $"{baseStation.userId}_web"; + motionDetection.to = $"{baseStation.deviceId}"; + motionDetection.action = "set"; + motionDetection.resource = $"cameras/{camera.deviceId}"; + motionDetection.transId = $"web!XXXXXXXX.XXXXXXXXXXXXXXXXXXXX"; + motionDetection.publishResponse = true; + PropertiesRequest propertiesRequest = new PropertiesRequest(); + propertiesRequest.motionSetupModeEnabled = true; + propertiesRequest.motionSetupModeSensitivity = 80; + motionDetection.properties = propertiesRequest; + + var body = JsonConvert.SerializeObject(motionDetection).ToString();*/ + + // Try to subscribe to motion for camera + /*MotionDetection motionDetection = new MotionDetection(); + motionDetection.from = $"{baseStation.userId}_web"; + motionDetection.to = $"{baseStation.deviceId}"; + motionDetection.action = "set"; + motionDetection.resource = $"subscriptions/{baseStation.userId}_web"; + motionDetection.transId = $"web!XXXXXXXX.XXXXXXXXXXXXXXXXXXXX"; + motionDetection.publishResponse = false; + PropertiesRequestMotionSubscription propertiesRequestMotionSubscription = new PropertiesRequestMotionSubscription(); + propertiesRequestMotionSubscription.devices = new string[] { camera.deviceId }; + motionDetection.properties = propertiesRequestMotionSubscription; + + var body = JsonConvert.SerializeObject(motionDetection).ToString(); + + HttpContent c = new StringContent(body, Encoding.UTF8, "application/json"); + client.DefaultRequestHeaders.Add("xcloudId", baseStation.xCloudId); + client.DefaultRequestHeaders.Add("Authorization", resultToken.token); + HttpResponseMessage result = await client.PostAsync($"{ _userDevicesNotifyUrl}/{baseStation.deviceId}", c); + if (result.IsSuccessStatusCode) + { + var response = await result.Content.ReadAsStringAsync(); + }*/ + } + }; evt.Disconnected += async (object sender, DisconnectEventArgs e) => { Console.WriteLine($"Retry: {e.ReconnectDelay} - Error: {e.Exception}"); await Task.Delay(e.ReconnectDelay); diff --git a/RPI Code/Arlo/FUNDING.yml b/RPI Code/Arlo/FUNDING.yml new file mode 100644 index 0000000..61da9c3 --- /dev/null +++ b/RPI Code/Arlo/FUNDING.yml @@ -0,0 +1,9 @@ +# These are supported funding model platforms + +github: [jeffreydwalter] +patreon: # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +custom: # Replace with a single custom sponsorship URL diff --git a/RPI Code/Arlo/ISSUE_TEMPLATE.md b/RPI Code/Arlo/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..0071113 --- /dev/null +++ b/RPI Code/Arlo/ISSUE_TEMPLATE.md @@ -0,0 +1,43 @@ +Please answer these questions before submitting your issue. Thanks! + + +### What version of Python are you using (`python -V`)? + + +### What operating system and processor architecture are you using (`python -c 'import platform; print(platform.uname());'`)? + + +### Which Python packages do you have installed (run the `pip freeze` or `pip3 freeze` command and paste output)? +``` +Paste your ouptut here +``` + +### Which version of ffmpeg are you using (`ffmpeg -version`)? +``` +Paste your output here +``` + +### Which Arlo hardware do you have (camera types - [Arlo, Pro, Q, etc.], basestation model, etc.)? + + +### What did you do? + +If possible, provide the steps you took to reproduce the issue. +A complete runnable program is good. (don't include your user/password or any sensitive info) +``` +Paste your ouptut here +``` + +### What did you expect to see? +``` +Paste your ouptut here +``` + +### What did you see instead? +``` +Paste your ouptut here +``` + +### Does this issue reproduce with the latest release? + + diff --git a/RPI Code/Arlo/LICENSE b/RPI Code/Arlo/LICENSE new file mode 100644 index 0000000..8dada3e --- /dev/null +++ b/RPI Code/Arlo/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/RPI Code/Arlo/Makefile b/RPI Code/Arlo/Makefile new file mode 100644 index 0000000..8ba1fa2 --- /dev/null +++ b/RPI Code/Arlo/Makefile @@ -0,0 +1,48 @@ +#red:=$(shell tput setaf 1) +#reset:=$(shell tput sgr0) + +init: + pip install -r requirements.txt + +clean: + rm -rf *.pyc + rm -rf dist + rm -rf build + rm -rf __pycache__ + rm -rf arlo.egg-info + +doc: + #pydoc -w ../arlo/arlo.py + #mv arlo.html arlo_api_doc.md + #git add arlo_api_doc.md + pdoc --overwrite --html --html-no-source --html-dir docs arlo.py + sed -i'.bak' 's/#sidebar{width:30%}#content{width:70%;/#sidebar{width:45%}#content{width:55%;/g' docs/arlo.html + rm docs/arlo.html.bak + python dev/html2text.py docs/arlo.html > docs/README.md + git add docs/* + +rev: + python dev/rev.py setup.py + git add setup.py + +commit: +ifndef message + $(error "Error: commit message required. Usage: make $(MAKECMDGOALS) message=''") +endif + + git add Makefile + + git add dev/* + + git add arlo.py + git add request.py + git add eventstream.py + git add requirements.txt + + git commit -m "$(message)" + git push + +release: clean rev doc commit + python3 setup.py sdist + python3 setup.py bdist_wheel --universal + twine upload --skip-existing dist/* diff --git a/RPI Code/Arlo/README.md b/RPI Code/Arlo/README.md new file mode 100644 index 0000000..db0b0c2 --- /dev/null +++ b/RPI Code/Arlo/README.md @@ -0,0 +1,134 @@ +![](logo.png) +# arlo ![](https://img.shields.io/badge/python-2.7%2C%203.4%2C%203.5%2C%203.6-blue.svg) +> Python module for interacting with Netgear's Arlo camera system. +> +>### Now in Golang! +>If you love the Go programming language, check out [arlo-golang](https://github.com/jeffreydwalter/arlo-golang). +>My goal is to bring parity to the Python version asap. If you know what you're doing in Go, I would appreciate any feedback on the >general structure of the library, and contributions, etc. + +--- +### GETTING STARTED +Check out the [API DOCS](https://github.com/jeffreydwalter/arlo/tree/master/docs) + +**IMPORTANT:** There is a regression in `sseclient 0.0.24` that breaks this package. Please ensure you have `seeclient 0.0.22` installed. + +**IMPORTANT:** Please ensure you don't have ANY other `sseclient` packages installed in addition to `sseclient 0.0.22`! This may cause this package to fail in unexpected ways. A common one that is known to cause issues is the `sseclient-py 1.7` package. If you have a hard requirement to have more than one, please let me know and we can look into making that work. + +**IMPORTANT:** my.arlo.com requires TLS 1.2 for their API. So, if you're getting ssl errors, it's most likely related to your version of openssl. You may need to upgrade your openssl library. +If you're running this library on OSX or macOS, they ship with `openssl v0.9.x` which does not support TLS 1.2. You should follow the instructions found [here](https://comeroutewithme.com/2016/03/13/python-osx-openssl-issue/) to upgrade your openssl library. + +--- +### Filing an Issue +Please read the [Issue Guidelines and Policies](https://github.com/jeffreydwalter/arlo/wiki/Issue-Guidelines-and-Policies) wiki page **BEFORE** you file an issue. Thanks. + +--- +## Install +```bash +# Install latest stable package +$ pip install arlo + +--or-- + +# Install from master branch +$ pip install git+https://github.com/jeffreydwalter/arlo +``` + +--- +This just a personal utility that I created out of necessity. It is by no means complete, although it does expose quite a bit of the Arlo interface in an easy to use Python pacakge. As such, this package does not come with unit tests (feel free to add them) or guarantees. +**All [contributions](https://github.com/jeffreydwalter/arlo/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) are welcome and appreciated!** +-- +**If you have a specific Arlo device that you want to improve support for, please consider sending me one! Since this project is solely maintained by yours truely and I don't have unlimited funds to support it, I can only really test and debug the code with the first gen Arlo cameras and basestation that I have. I also highly encourage and appreciate Pull Requests!** + +**Please, feel free to [contribute](https://github.com/jeffreydwalter/arlo/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) to this repo or buy Jeff a beer!** [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=R77B7UXMLA6ML&lc=US&item_name=Jeff%20Needs%20Beer&item_number=buyjeffabeer¤cy_code=USD&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted) + +--- +### Generous Benefactors (Thank you!) +* [apsteinmetz](https://github.com/apsteinmetz) - 🍺 +* [mhallikainen](https://github.com/mhallikainen) - 🍺🍺 +* [tinsheep](https://github.com/tinsheep) - 🍺🍺 +* [cubewot](https://github.com/cubewot) - 🍺🍺 +* [imopen](https://github.com/imopen) - 🍺 +* [notalifeform](https://github.com/notalifeform) - 🍺🍺 +* [anonymous](https://github.com/jeffreydwalter/arlo) - 🍺🍺🍺🍺 +* [kewashi](https://github.com/kewashi) - 🍺 + +--- +### Awesomely Smart Contributors (Thank you!) +* [alvin-chang](https://github.com/alvin-chang) - Dec 15, 2019 - Updated some print statements to work with Python 3 in an example script. +* [pabloNZ](https://github.com/pabloNZ) - Jun 4, 2019 - Added the Arlo doorbell, Ultra camera and basestation schemas to the wiki. +* [m3ntalsp00n](https://github.com/m3ntalsp00n) - May 18, 2019 - Expanded ArloQ device support. +* [burken-](https://github.com/burken-) - Apr 17, 2019 - Fixed arming/disarming ArloQ devices. +* [m0urs](https://github.com/m0urs) - Apr 16, 2019 - Updated fqdn to new Arlo domain. +* [kimc78](https://github.com/kimc78) - Aug 16, 2018 - Added method to get CVR recording list. +* [jurgenweber](https://github.com/jurgenweber) - Apr 25, 2018 - Added Arlo Baby APIs! +* [pliablepixels](https://github.com/pliablepixels) - Apr 3, 2018 - Fixed up issues with the README. +* [manluk](https://github.com/manluk) - Mar 2, 2018 - Squashed a couple of bugs. +* [notalifeform](https://github.com/notalifeform) - Feb 10, 2018 - Fixed bug and formatting in example script. +* [erosen](https://github.com/erosen) - Jan 27, 2018 - Added the ArloQ camera schema to the wiki. +* [deanmcguire](https://github.com/deanmcguire) - Dec 7, 2017 - Unravelled the mysteries of RTSP streaming video. +* [andijakl](https://github.com/andijakl) - Jul 24, 2017 - Added Python 3 support and cleaned up examples. +* [cemeyer2](https://github.com/cemeyer2) - Nov 26, 2016 - Fixed setup issues. +* [LenShustek](https://github.com/LenShustek) - Sep 14, 2016, - Added Logout(). + +If You'd like to make a diffrence in the world and get your name on this most prestegious list, have a look at our [help wanted](https://github.com/jeffreydwalter/arlo/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) section! + +After installing all of the required libraries, you can import and use this library like so: + +```python +from arlo import Arlo + +from datetime import timedelta, date +import datetime +import sys + +USERNAME = 'user@example.com' +PASSWORD = 'supersecretpassword' + +try: + # Instantiating the Arlo object automatically calls Login(), which returns an oAuth token that gets cached. + # Subsequent successful calls to login will update the oAuth token. + arlo = Arlo(USERNAME, PASSWORD) + # At this point you're logged into Arlo. + + today = (date.today()-timedelta(days=0)).strftime("%Y%m%d") + seven_days_ago = (date.today()-timedelta(days=7)).strftime("%Y%m%d") + + # Get all of the recordings for a date range. + library = arlo.GetLibrary(seven_days_ago, today) + + # Iterate through the recordings in the library. + for recording in library: + + videofilename = datetime.datetime.fromtimestamp(int(recording['name'])//1000).strftime('%Y-%m-%d %H-%M-%S') + ' ' + recording['uniqueId'] + '.mp4' + ## + # The videos produced by Arlo are pretty small, even in their longest, best quality settings, + # but you should probably prefer the chunked stream (see below). + ### + # # Download the whole video into memory as a single chunk. + # video = arlo.GetRecording(recording['presignedContentUrl']) + # with open('videos/'+videofilename, 'wb') as f: + # f.write(video) + # f.close() + # Or: + # + # Get video as a chunked stream; this function returns a generator. + stream = arlo.StreamRecording(recording['presignedContentUrl']) + with open('videos/'+videofilename, 'wb') as f: + for chunk in stream: + f.write(chunk) + f.close() + + print('Downloaded video '+videofilename+' from '+recording['createdDate']+'.') + + # Delete all of the videos you just downloaded from the Arlo library. + # Notice that you can pass the "library" object we got back from the GetLibrary() call. + result = arlo.BatchDeleteRecordings(library) + + # If we made it here without an exception, then the videos were successfully deleted. + print('Batch deletion of videos completed successfully.') + +except Exception as e: + print(e) +``` + +**For more code examples check out the [wiki](https://github.com/jeffreydwalter/arlo/wiki)** diff --git a/RPI Code/Arlo/__pycache__/arlo.cpython-37.pyc b/RPI Code/Arlo/__pycache__/arlo.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..39ccdfb30a503f8909954cb7b8dd9c32adc42b3f GIT binary patch literal 79431 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%0D0Ix>5imdEQB?-vd@p3ShWL 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 literal 0 HcmV?d00001 diff --git a/RPI Code/Arlo/__pycache__/request.cpython-37.pyc b/RPI Code/Arlo/__pycache__/request.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9b6a9679a7be4fe6e9790791e12385b905a8b946 GIT binary patch literal 1544 zcmbVMy>i?(5C#a4#GOvIEGs|OjApJhT}Vzc<06w}oMddLQZ&(}VPTjiaM4M;<9WfG z*b=Wv)T&LbE0Zck9wN_xHdS6BRd#tijpC?DFtgaj?gIGm?UG-tuXh=a;qRY5|0QPZ z4_d5-MB_1vyN{D$h8HX)9Ta>VqycBom=Q*vGb63|QKZuFLzaYpVm8}PxbJ*57#utS zC?O};YJ_M!Msd$^vMl9>rGeq8Fo6;043Ci`Hex0;;V?9jiO+c&UU{TZrh}en=@|;s zHEXzzuhu=Yj%#IZA(-1f{>HwVSy!FjAvFCXTTCtV^CELD=LmoX=>X+R6n6`!ie2&r ztCV)!!y_c;{hUPe>9 zdw2TAE3HS9QKj{JbRS*J_~}NAb9WW=;@OkI)0_^Z(Lo`*!T1{*HF9iOSF=}z5;6)uDvdJUNl$iOn$qbg6CcF^yc(RSL2xBjrn7AIR<4d_fYEXv@ zHdl27uWNP4YGvYkSa{s^lTo!giQrfCse8!OWV?z?=Q6J8mS*2V)UoMa1V3Pvk;%1v zpngL-qiF`&*!lR#X2yc^k*mOF#vEho+<3aID2_|8^dOlOss)6!Lb1G15` zdRT&XRaVU$+{QGZg7^r%*K_@;#(+Dt2YD1RUo+xdg$o}*E*ryniB*Xr0LYA2e#0t*(IyF;mCJW z{Y8Wv2Q9}Ob}%K0nis(PXupLV2-91D;V$|fqOAIIZPk}ZUQRU9Abr)0o4EKVkVUwJ zL7!66!U%7KaT5Z1*heV;LV%U~uLw5&7lL 9: r = chr(r+55) + else: r = str(r) + result = r + result + + # And now the part: + if d == 0: return result + + result += '.' + count = 0 + while d: + d = d * 16 + w, d = divmod(d, 1) + w = int(w) + if w > 9: w = chr(w+55) + else: w = str(w) + result += w + count += 1 + if count > MAXHEXADECIMALS: break + + return result + + now = datetime.today() + return trans_type+"!" + float2hex(random.random() * math.pow(2, 32)).lower() + "!" + str(int((time.mktime(now.timetuple())*1e3 + now.microsecond/1e3))) + + def Login(self, username, password): + """ + This call returns the following: + { + "userId":"XXX-XXXXXXX", + "email":"user@example.com", + "token":"2_5HicFJMXXXXX-S_7IuK2EqOUHXXXXXXXXXXX1CXKWTThgU18Va_XXXXXX5S00hUafv3PV_if_Bl_rhiFsDHYwhxI3CxlVnR5f3q2XXXXXX-Wnt9F7D82uN1f4cXXXXX-FMUsWF_6tMBqwn6DpzOaIB7ciJrnr2QJyKewbQouGM6", + "paymentId":"XXXXXXXX", + "authenticated":1472961381, + "accountStatus":"registered", + "serialNumber":"XXXXXXXXXXXXX", + "countryCode":"US", + "tocUpdate":false, + "policyUpdate":false, + "validEmail":true + } + """ + self.username = username + self.password = password + + self.request = Request() + + body = self.request.post('https://my.arlo.com/hmsweb/login/v2', {'email': self.username, 'password': self.password}) + + headers = { + 'DNT': '1', + 'schemaVersion': '1', + 'Host': 'my.arlo.com', + 'Content-Type': 'application/json; charset=utf-8;', + 'Referer': 'https://my.arlo.com/', + 'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_1_2 like Mac OS X) AppleWebKit/604.3.5 (KHTML, like Gecko) Mobile/15B202 NETGEAR/v1 (iOS Vuezone)', + 'Authorization': body['token'] + } + self.request.session.headers.update(headers) + + self.user_id = body['userId'] + return body + + def Logout(self): + event_streams = self.event_streams.copy() + for basestation_id in event_streams.keys(): + self.Unsubscribe(basestation_id) + return self.request.put('https://my.arlo.com/hmsweb/logout') + + def Subscribe(self, basestation): + """ + Arlo uses the EventStream interface in the browser to do pub/sub style messaging. + Unfortunately, this appears to be the only way Arlo communicates these messages. + + This function makes the initial GET request to /subscribe, which returns the EventStream socket. + Once we have that socket, the API requires a POST request to /notify with the "subscriptionsresource. + This call "registersthe device (which should be the basestation) so that events will be sent to the EventStream + when subsequent calls to /notify are made. + + Since this interface is asynchronous, and this is a quick and dirty hack to get this working, I'm using a thread + to listen to the EventStream. This thread puts events into a queue. Some polling is required (see NotifyAndGetResponse()) because + the event messages aren't guaranteed to be delivered in any specific order, but I wanted to maintain a synchronous style API. + + You generally shouldn't need to call Subscribe() directly, although I'm leaving it "publicfor now. + """ + basestation_id = basestation.get('deviceId') + + def Register(self): + if basestation_id in self.event_streams and self.event_streams[basestation_id].connected: + self.Notify(basestation, {"action":"set","resource":"subscriptions/"+self.user_id+"_web","publishResponse":False,"properties":{"devices":[basestation_id]}}) + event = self.event_streams[basestation_id].Get() + if event is None or self.event_streams[basestation_id].event_stream_stop_event.is_set(): + return None + elif event: + self.event_streams[basestation_id].Register() + return event + + def QueueEvents(self, event_stream, stop_event): + for event in event_stream: + if event is None or stop_event.is_set(): + return None + + response = json.loads(event.data) + if basestation_id in self.event_streams: + if self.event_streams[basestation_id].connected: + if response.get('action') == 'logout': + self.event_streams[basestation_id].Disconnect() + return None + else: + self.event_streams[basestation_id].queue.put(response) + elif response.get('status') == 'connected': + self.event_streams[basestation_id].Connect() + + def Heartbeat(self, stop_event): + while not stop_event.wait(30.0): + try: + self.Ping(basestation) + except: + pass + + if basestation_id not in self.event_streams or not self.event_streams[basestation_id].connected: + self.event_streams[basestation_id] = EventStream(QueueEvents, Heartbeat, args=(self, )) + self.event_streams[basestation_id].Start() + while not self.event_streams[basestation_id].connected and not self.event_streams[basestation_id].event_stream_stop_event.is_set(): + time.sleep(0.5) + + if not self.event_streams[basestation_id].registered: + Register(self) + + def Unsubscribe(self, basestation): + """ This method stops the EventStream subscription and removes it from the event_stream collection. """ + if isinstance(basestation, (text_type, string_types)): + basestation_id = basestation + else: + basestation_id = basestation.get('deviceId') + if basestation_id in self.event_streams: + if self.event_streams[basestation_id].connected: + self.request.get('https://my.arlo.com/hmsweb/client/unsubscribe') + self.event_streams[basestation_id].Disconnect() + + del self.event_streams[basestation_id] + + def Notify(self, basestation, body): + """ + The following are examples of the json you would need to pass in the body of the Notify() call to interact with Arlo: + + ############################################################################################################################## + ############################################################################################################################## + NOTE: While you can call Notify() directly, responses from these notify calls are sent to the EventStream (see Subscribe()), + and so it's better to use the Get/Set methods that are implemented using the NotifyAndGetResponse() method. + ############################################################################################################################## + ############################################################################################################################## + + Set System Mode (Armed, Disarmed) - {"from":"XXX-XXXXXXX_web","to":"XXXXXXXXXXXXX","action":"set","resource":"modes","transId":"web!XXXXXXXX.XXXXXXXXXXXXXXXXXXXX","publishResponse":true,"properties":{"active":"mode0"}} + Set System Mode (Calendar) - {"from":"XXX-XXXXXXX_web","to":"XXXXXXXXXXXXX","action":"set","resource":"schedule","transId":"web!XXXXXXXX.XXXXXXXXXXXXXXXXXXXX","publishResponse":true,"properties":{"active":true}} + Configure The Schedule (Calendar) - {"from":"XXX-XXXXXXX_web","to":"XXXXXXXXXXXXX","action":"set","resource":"schedule","transId":"web!XXXXXXXX.XXXXXXXXXXXXXXXXXXXX","publishResponse":true,"properties":{"schedule":[{"modeId":"mode0","startTime":0},{"modeId":"mode2","startTime":28800000},{"modeId":"mode0","startTime":64800000},{"modeId":"mode0","startTime":86400000},{"modeId":"mode2","startTime":115200000},{"modeId":"mode0","startTime":151200000},{"modeId":"mode0","startTime":172800000},{"modeId":"mode2","startTime":201600000},{"modeId":"mode0","startTime":237600000},{"modeId":"mode0","startTime":259200000},{"modeId":"mode2","startTime":288000000},{"modeId":"mode0","startTime":324000000},{"modeId":"mode0","startTime":345600000},{"modeId":"mode2","startTime":374400000},{"modeId":"mode0","startTime":410400000},{"modeId":"mode0","startTime":432000000},{"modeId":"mode0","startTime":518400000}]} + Create Mode - + {"from":"XXX-XXXXXXX_web","to":"XXXXXXXXXXXXX","action":"add","resource":"rules","transId":"web!XXXXXXXX.XXXXXXXXXXXXXXXXXXXX","publishResponse":true,"properties":{"name":"Record video on Camera 1 if Camera 1 detects motion","id":"ruleNew","triggers":[{"type":"pirMotionActive","deviceId":"XXXXXXXXXXXXX","sensitivity":80}],"actions":[{"deviceId":"XXXXXXXXXXXXX","type":"recordVideo","stopCondition":{"type":"timeout","timeout":15}},{"type":"sendEmailAlert","recipients":["__OWNER_EMAIL__"]},{"type":"pushNotification"}]}} + {"from":"XXX-XXXXXXX_web","to":"XXXXXXXXXXXXX","action":"add","resource":"modes","transId":"web!XXXXXXXX.XXXXXXXXXXXXXXXXXXXX","publishResponse":true,"properties":{"name":"Test","rules":["rule3"]}} + Delete Mode - {"from":"XXX-XXXXXXX_web","to":"XXXXXXXXXXXXX","action":"delete","resource":"modes/mode3","transId":"web!XXXXXXXX.XXXXXXXXXXXXXXXXXXXX","publishResponse":true} + Camera Off - {"from":"XXX-XXXXXXX_web","to":"XXXXXXXXXXXXX","action":"set","resource":"cameras/XXXXXXXXXXXXX","transId":"web!XXXXXXXX.XXXXXXXXXXXXXXXXXXXX","publishResponse":true,"properties":{"privacyActive":false}} + Night Vision On - {"from":"XXX-XXXXXXX_web","to":"XXXXXXXXXXXXX","action":"set","resource":"cameras/XXXXXXXXXXXXX","transId":"web!XXXXXXXX.XXXXXXXXXXXXXXXXXXXX","publishResponse":true,"properties":{"zoom":{"topleftx":0,"toplefty":0,"bottomrightx":1280,"bottomrighty":720},"mirror":true,"flip":true,"nightVisionMode":1,"powerSaveMode":2}} + Motion Detection Test - {"from":"XXX-XXXXXXX_web","to":"XXXXXXXXXXXXX","action":"set","resource":"cameras/XXXXXXXXXXXXX","transId":"web!XXXXXXXX.XXXXXXXXXXXXXXXXXXXX","publishResponse":true,"properties":{"motionSetupModeEnabled":true,"motionSetupModeSensitivity":80}} + + device_id = locations.data.uniqueIds + + System Properties: ("resource":"modes") + active (string) - Mode Selection (mode2 = All Motion On, mode1 = Armed, mode0 = Disarmed, etc.) + + System Properties: ("resource":"schedule") + active (bool) - Mode Selection (true = Calendar) + + Camera Properties: ("resource":"cameras/{id}") + privacyActive (bool) - Camera On/Off + zoom (topleftx (int), toplefty (int), bottomrightx (int), bottomrighty (int)) - Camera Zoom Level + mirror (bool) - Mirror Image (left-to-right or right-to-left) + flip (bool) - Flip Image Vertically + nightVisionMode (int) - Night Mode Enabled/Disabled (1, 0) + powerSaveMode (int) - PowerSaver Mode (3 = Best Video, 2 = Optimized, 1 = Best Battery Life) + motionSetupModeEnabled (bool) - Motion Detection Setup Enabled/Disabled + motionSetupModeSensitivity (int 0-100) - Motion Detection Sensitivity + """ + basestation_id = basestation.get('deviceId') + + body['transId'] = self.genTransId() + body['from'] = self.user_id+'_web' + body['to'] = basestation_id + + print(body) + + self.request.post('https://my.arlo.com/hmsweb/users/devices/notify/'+body['to'], body, headers={"xcloudId":basestation.get('xCloudId')}) + return body.get('transId') + + def NotifyAndGetResponse(self, basestation, body, timeout=120): + basestation_id = basestation.get('deviceId') + + self.Subscribe(basestation) + + if basestation_id in self.event_streams and self.event_streams[basestation_id].connected and self.event_streams[basestation_id].registered: + transId = self.Notify(basestation, body) + + event = self.event_streams[basestation_id].Get(timeout=timeout) + if event is None or self.event_streams[basestation_id].event_stream_stop_event.is_set(): + return None + + while basestation_id in self.event_streams and self.event_streams[basestation_id].connected and self.event_streams[basestation_id].registered: + tid = event.get('transId', '') + if tid != transId: + if tid.startswith(self.TRANSID_PREFIX): + self.event_streams[basestation_id].queue.put(event) + + event = self.event_streams[basestation_id].Get(timeout=timeout) + if event is None or self.event_streams[basestation_id].event_stream_stop_event.is_set(): + return None + else: break + + return event + + def Ping(self, basestation): + basestation_id = basestation.get('deviceId') + return self.NotifyAndGetResponse(basestation, {"action":"set","resource":"subscriptions/"+self.user_id+"_web","publishResponse":False,"properties":{"devices":[basestation_id]}}) + + def SubscribeToMotionEvents(self, basestation, callback, timeout=120): + """ + Use this method to subscribe to motion events. You must provide a callback function which will get called once per motion event. + + The callback function should have the following signature: + def callback(self, event) + + This is an example of handling a specific event, in reality, you'd probably want to write a callback for HandleEvents() + that has a big switch statement in it to handle all the various events Arlo produces. + """ + def callbackwrapper(self, event): + if event.get('properties', {}).get('motionDetected'): + callback(self, event) + + self.HandleEvents(basestation, callbackwrapper, timeout) + + def HandleEvents(self, basestation, callback, timeout=120): + """ + Use this method to subscribe to the event stream and provide a callback that will be called for event event received. + This function will allow you to potentially write a callback that can handle all of the events received from the event stream. + """ + if not callable(callback): + raise Exception('The callback(self, event) should be a callable function.') + + basestation_id = basestation.get('deviceId') + + self.Subscribe(basestation) + if basestation_id in self.event_streams and self.event_streams[basestation_id].connected and self.event_streams[basestation_id].registered: + while basestation_id in self.event_streams and self.event_streams[basestation_id].connected: + event = self.event_streams[basestation_id].Get(timeout=timeout) + if event is None or self.event_streams[basestation_id].event_stream_stop_event.is_set(): + return None + + # If this event has is of resource type "subscriptions", then it's a ping reply event. + # For now, these types of events will be requeued, since they are generated in response to and expected as a reply by the Ping() method. + # HACK: Take a quick nap here to give the Ping() method's thread a chance to get the queued event. + if event.get('resource', '').startswith('subscriptions'): + self.event_streams[basestation_id].queue.put(event) + time.sleep(0.05) + else: + response = callback(self, event) + # NOTE: Not ideal, but this allows you to look for a specific event and break if you want to return it. + if response is not None: + return response + + def TriggerAndHandleEvent(self, basestation, trigger, callback, timeout=120): + """ + Use this method to subscribe to the event stream and provide a callback that will be called for event event received. + This function will allow you to potentially write a callback that can handle all of the events received from the event stream. + NOTE: Use this function if you need to run some code after subscribing to the eventstream, but before your callback to handle the events runs. + """ + if not callable(trigger): + raise Exception('The trigger(self, camera) should be a callable function.') + if not callable(callback): + raise Exception('The callback(self, event) should be a callable function.') + + self.Subscribe(basestation) + trigger(self) + + # NOTE: Calling HandleEvents() calls Subscribe() again, which basically turns into a no-op. Hackie I know, but it cleans up the code a bit. + return self.HandleEvents(basestation, callback, timeout) + + def GetBaseStationState(self, basestation): + return self.NotifyAndGetResponse(basestation, {"action":"get","resource":"basestation","publishResponse":False}) + + def GetCameraState(self, basestation): + return self.NotifyAndGetResponse(basestation, {"action":"get","resource":"cameras","publishResponse":False}) + + def GetRules(self, basestation): + return self.NotifyAndGetResponse(basestation, {"action":"get","resource":"rules","publishResponse":False}) + + def GetSmartFeatures(self): + return self.request.get('https://my.arlo.com/hmsweb/users/subscription/smart/features') + + def GetSmartAlerts(self, camera): + return self.request.get('https://my.arlo.com/hmsweb/users/devices/'+camera.get('uniqueId')+'/smartalerts') + + def GetAutomationActivityZones(self, camera): + return self.request.get('https://my.arlo.com/hmsweb/users/devices/'+camera.get('uniqueId')+'/activityzones') + + def RestartBasestation(self, basestation): + return self.request.post('https://my.arlo.com/hmsweb/users/devices/restart', {"deviceId":basestation.get('deviceId')}) + + def SetAutomationActivityZones(self, camera, zone, coords, color): + """ + An activity zone is the area you draw in your video in the UI to tell Arlo what part of the scene to "watch". + This method takes 4 arguments. + camera: the camera you want to set an activity zone for. + name: "Zone 1" - the name of your activity zone. + coords: [{"x":0.37946943483275664,"y":0.3790983606557377},{"x":0.8685121107266436,"y":0.3790983606557377},{"x":0.8685121107266436,"y":1},{"x":0.37946943483275664,"y":1}] - these coordinates are the bonding box for the activity zone. + color: 45136 - the color for your bounding box. + """ + return self.request.post('https://my.arlo.com/hmsweb/users/devices/'+camera.get('uniqueId')+'/activityzones', {"name": zone,"coords": coords, "color": color}) + + def GetAutomationDefinitions(self): + return self.request.get('https://my.arlo.com/hmsweb/users/automation/definitions', {'uniqueIds':'all'}) + + def GetCalendar(self, basestation): + return self.NotifyAndGetResponse(basestation, {"action":"get","resource":"schedule","publishResponse":False}) + + def DeleteMode(self, device, mode): + """ device can be any object that has parentId == deviceId. i.e., not a camera """ + parentId = device.get('parentId', None) + if device['deviceType'] == 'arlobridge': + return self.request.delete('https://my.arlo.com/hmsweb/users/locations/'+device.get('uniqueId')+'/modes/'+mode) + elif not parentId or device.get('deviceId') == parentId: + return self.NotifyAndGetResponse(basestation, {"action":"delete","resource":"modes/"+mode,"publishResponse":True}) + else: + raise Exception('Only parent device modes and schedules can be deleted.'); + + def GetModes(self, basestation): + """ DEPRECATED: This is the older API for getting the "mode". It still works, but GetModesV2 is the way the Arlo software does it these days. """ + return self.NotifyAndGetResponse(basestation, {"action":"get","resource":"modes","publishResponse":False}) + + def GetModesV2(self): + """ + This is the newer API for getting the "mode". This method also returns the schedules. + Set a non-schedule mode to be active: {"activeAutomations":[{"deviceId":"XXXXXXXXXXXXX","timestamp":1532015622105,"activeModes":["mode1"],"activeSchedules":[]}]} + Set a schedule to be active: {"activeAutomations":[{"deviceId":"XXXXXXXXXXXXX","timestamp":1532015790139,"activeModes":[],"activeSchedules":["schedule.1"]}]} + """ + return self.request.get('https://my.arlo.com/hmsweb/users/devices/automation/active') + + def CustomMode(self, device, mode, schedules=[]): + """ device can be any object that has parentId == deviceId. i.e., not a camera """ + if(device["deviceType"].startswith("arloq")): + return self.NotifyAndGetResponse(device, {"from":self.user_id+"_web", "to": device.get("parentId"), "action":"set","resource":"modes", "transId": self.genTransId(),"publishResponse":True,"properties":{"active":mode}}) + else: + return self.request.post('https://my.arlo.com/hmsweb/users/devices/automation/active', {'activeAutomations':[{'deviceId':device.get('deviceId'),'timestamp':self.to_timestamp(datetime.now()),'activeModes':[mode],'activeSchedules':schedules}]}) + + def Arm(self, device): + return self.CustomMode(device, "mode1") + + def Disarm(self, device): + return self.CustomMode(device, "mode0") + + def Calendar(self, basestation, active=True): + """ + DEPRECATED: This API appears to still do stuff, but I don't see it called in the web UI anymore when switching the mode to a schedule. + + NOTE: The Arlo API seems to disable calendar mode when switching to other modes, if it's enabled. + You should probably do the same, although, the UI reflects the switch from calendar mode to say armed mode without explicitly setting calendar mode to inactive. + """ + return self.NotifyAndGetResponse(basestation, {"action":"set","resource":"schedule","publishResponse":True,"properties":{"active":active}}) + + def SetSchedule(self, basestation, schedule): + """ + The following json is what was sent to the API when I edited my schedule. It contains all of the data necessary to configure a whole week. It's a little convoluted, but you can just play around with the scheduler in Chrome and watch the schema that gets sent. + + { + "schedule": [ + { + "duration": 600, + "startActions": { + "disableModes": [ + "mode0" + ], + "enableModes": [ + "mode1" + ] + }, + "days": [ + "Mo", + "Tu", + "We", + "Th", + "Fr", + "Sa", + "Su" + ], + "startTime": 0, + "type": "weeklyAction", + "endActions": { + "disableModes": [ + "mode1" + ], + "enableModes": [ + "mode0" + ] + } + }, + { + "duration": 360, + "startActions": { + "disableModes": [ + "mode0" + ], + "enableModes": [ + "mode2" + ] + }, + "days": [ + "Mo", + "Tu", + "We", + "Th", + "Fr", + "Sa", + "Su" + ], + "startTime": 1080, + "type": "weeklyAction", + "endActions": { + "disableModes": [ + "mode2" + ], + "enableModes": [ + "mode0" + ] + } + }, + { + "duration": 480, + "startActions": { + "disableModes": [ + "mode0" + ], + "enableModes": [ + "mode3" + ] + }, + "days": [ + "Tu" + ], + "startTime": 600, + "type": "weeklyAction", + "endActions": { + "disableModes": [ + "mode3" + ], + "enableModes": [ + "mode0" + ] + } + } + ], + "name": "", + "id": "schedule.1", + "enabled": true + } + """ + return self.request.post('https://my.arlo.com/hmsweb/users/locations/'+basestation.get('uniqueId')+'/schedules', ) + + def AdjustBrightness(self, basestation, camera, brightness=0): + """ + NOTE: Brightness is between -2 and 2 in increments of 1 (-2, -1, 0, 1, 2). + Setting it to an invalid value has no effect. + + Returns: + { + "action": "is", + "from": "XXXXXXXXXXXXX", + "properties": { + "brightness": -2 + }, + "resource": "cameras/XXXXXXXXXXXXX", + "to": "336-XXXXXXX_web", + "transId": "web!XXXXXXXX.389518!1514956240683" + } + """ + return self.NotifyAndGetResponse(basestation, {"action":"set","resource":"cameras/"+camera.get('deviceId'),"publishResponse":True,"properties":{"brightness":brightness}}) + + def ToggleCamera(self, basestation, camera, active=True): + """ + active: True - Camera is off. + active: False - Camera is on. + """ + return self.NotifyAndGetResponse(basestation, {"action":"set","resource":"cameras/"+camera.get('deviceId'),"publishResponse":True,"properties":{"privacyActive":active}}) + + def PushToTalk(self, camera): + return self.request.get('https://my.arlo.com/hmsweb/users/devices/'+camera.get('uniqueId')+'/pushtotalk') + + """ General alert toggles """ + def SetMotionAlertsOn(self, basestation, sensitivity=5): + return self.NotifyAndGetResponse(basestation, {"action":"set","resource":"cameras/"+basestation.get('deviceId'),"publishResponse":True,"properties":{"motionDetection":{"armed":True,"sensitivity":sensitivity,"zones":[]}}}) + + def SetMotionAlertsOff(self, basestation, sensitivity=5): + return self.NotifyAndGetResponse(basestation, {"action":"set","resource":"cameras/"+basestation.get('deviceId'),"publishResponse":True,"properties":{"motionDetection":{"armed":False,"sensitivity":sensitivity,"zones":[]}}}) + + def SetAudioAlertsOn(self, basestation, sensitivity=3): + return self.NotifyAndGetResponse(basestation, {"action":"set","resource":"cameras/"+basestation.get('deviceId'),"publishResponse":True,"properties":{"audioDetection":{"armed":True,"sensitivity":sensitivity}}}) + + def SetAudioAlertsOff(self, basestation, sensitivity=3): + return self.NotifyAndGetResponse(basestation, {"action":"set","resource":"cameras/"+basestation.get('deviceId'),"publishResponse":True,"properties":{"audioDetection":{"armed":False,"sensitivity":sensitivity}}}) + + def AlertNotificationMethods(self, basestation, action="disabled", email=False, push=False): + """ action : disabled OR recordSnapshot OR recordVideo """ + return self.NotifyAndGetResponse(basestation, {"action":"set","resource":"cameras/"+basestation.get('deviceId'),"publishResponse":True,"properties":{"eventAction":{"actionType":action,"stopType":"timeout","timeout":15,"emailNotification":{"enabled":email,"emailList":["__OWNER_EMAIL__"]},"pushNotification":push}}}) + + """ Arlo Baby Audio Control """ + def GetAudioPlayback(self, basestation): + return self.NotifyAndGetResponse(basestation, {"action":"get","resource":"audioPlayback","publishResponse":False}) + + def PlayTrack(self, basestation, track_id="2391d620-e491-4412-99f6-e9a40d6046ed", position=0): + """ Defaulting to 'hugh little baby', which is a supplied track. I hope the ID is the same for all. """ + return self.Notify(basestation, {"action":"playTrack","resource":"audioPlayback/player","properties":{"trackId":track_id,"position":position}}) + + def PauseTrack(self, basestation): + return self.Notify(basestation, {"action":"pause","resource":"audioPlayback/player"}) + + def UnPauseTrack(self, basestation): + return self.Notify(basestation, {"action":"play","resource":"audioPlayback/player"}) + + def SkipTrack(self, basestation): + return self.Notify(basestation, {"action":"nextTrack","resource":"audioPlayback/player"}) + + def SetSleepTimerOn(self, basestation, time=calendar.timegm(time.gmtime()) + 300, timediff=0): + return self.NotifyAndGetResponse(basestation, {"action":"set","resource":"audioPlayback/config","publishResponse":True,"properties":{"config":{"sleepTime":time,"sleepTimeRel":timediff}}}) + + def SetSleepTimerOff(self, basestation, time=0, timediff=300): + return self.NotifyAndGetResponse(basestation, {"action":"set","resource":"audioPlayback/config","publishResponse":True,"properties":{"config":{"sleepTime": time,"sleepTimeRel":timediff}}}) + + def SetLoopBackModeContinuous(self, basestation): + return self.NotifyAndGetResponse(basestation, {"action":"set","resource":"audioPlayback/config","publishResponse":True,"properties":{"config":{"loopbackMode":"continuous"}}}) + + def SetLoopBackModeSingleTrack(self, basestation): + return self.NotifyAndGetResponse(basestation, {"action":"set","resource":"audioPlayback/config","publishResponse":True,"properties":{"config":{"loopbackMode":"singleTrack"}}}) + + def SetShuffleOn(self, basestation): + return self.NotifyAndGetResponse(basestation, {"action":"set","resource":"audioPlayback/config","publishResponse":True,"properties":{"config":{"shuffleActive":True}}}) + + def SetShuffleOff(self, basestation): + return self.NotifyAndGetResponse(basestation, {"action":"set","resource":"audioPlayback/config","publishResponse":True,"properties":{"config":{"shuffleActive":False}}}) + + def SetVolume(self, basestation, mute=False, volume=50): + return self.NotifyAndGetResponse(basestation, {"action":"set","resource":"cameras/"+basestation.get('deviceId'),"publishResponse":True,"properties":{"speaker":{"mute":mute,"volume":volume}}}) + + """ Baby Arlo Nightlight, (current state is in the arlo.GetCameraState(cameras[0]["properties"][0]["nightLight"]) """ + def SetNightLightOn(self, basestation): + return self.NotifyAndGetResponse(basestation, {"action":"set","resource":"cameras/"+basestation.get('deviceId'),"publishResponse":True,"properties":{"nightLight":{"enabled":True}}}) + + def SetNightLightOff(self, basestation): + return self.NotifyAndGetResponse(basestation, {"action":"set","resource":"cameras/"+basestation.get('deviceId'),"publishResponse":True,"properties":{"nightLight":{"enabled":False}}}) + + def SetNightLightBrightness(self, basestation, level=200): + return self.NotifyAndGetResponse(basestation, {"action":"set","resource":"cameras/"+basestation.get('deviceId'),"publishResponse":True,"properties":{"nightLight":{"brightness":level}}}) + + def SetNightLightMode(self, basestation, mode="rainbow"): + """ mode: rainbow or rgb. """ + return self.NotifyAndGetResponse(basestation, {"action":"set","resource":"cameras/"+basestation.get('deviceId'),"publishResponse":True,"properties":{"nightLight":{"mode":mode}}}) + + def SetNightLightColor(self, basestation, red=255, green=255, blue=255): + return self.NotifyAndGetResponse(basestation, {"action":"set","resource":"cameras/"+basestation.get('deviceId'),"publishResponse":True,"properties":{"nightLight":{"rgb":{"blue":blue,"green":green,"red":red}}}}) + + def SetNightLightTimerOn(self, basestation, time=calendar.timegm(time.gmtime()) + 300, timediff=0): + return self.NotifyAndGetResponse(basestation, {"action":"set","resource":"cameras/"+basestation.get('deviceId'),"publishResponse":True,"properties":{"nightLight":{"sleepTime":time,"sleepTimeRel":timediff}}}) + + def SetNightLightTimerOff(self, basestation, time=0, timediff=300): + return self.NotifyAndGetResponse(basestation, {"action":"set","resource":"cameras/"+basestation.get('deviceId'),"publishResponse":True,"properties":{"nightLight":{"sleepTime":time,"sleepTimeRel":timediff}}}) + + """ Baby Arlo Sensors """ + def GetCameraTempReading(self, basestation): + return self.NotifyAndGetResponse(basestation, {"action":"get","resource":"cameras/"+basestation.get('deviceId')+"/ambientSensors/history","publishResponse":False}) + + def GetSensorConfig(self, basestation): + return self.NotifyAndGetResponse(basestation, {"action":"get","resource":"cameras/"+basestation.get('deviceId')+"/ambientSensors/config","publishResponse":False}) + + def SetAirQualityAlertOn(self, basestation): + return self.NotifyAndGetResponse(basestation, {"action":"set","resource":"cameras/"+basestation.get('deviceId')+"/ambientSensors/config","publishResponse":True,"properties":{"airQuality":{"alertsEnabled":True}}}) + + def SetAirQualityAlertOff(self, basestation): + return self.NotifyAndGetResponse(basestation, {"action":"set","resource":"cameras/"+basestation.get('deviceId')+"/ambientSensors/config","publishResponse":True,"properties":{"airQuality":{"alertsEnabled":False}}}) + + def SetAirQualityAlertThresholdMin(self, basestation, number=400): + return self.NotifyAndGetResponse(basestation, {"action":"set","resource":"cameras/"+basestation.get('deviceId')+"/ambientSensors/config","publishResponse":True,"properties":{"airQuality":{"minThreshold":number}}}) + + def SetAirQualityAlertThresholdMax(self, basestation, number=700): + return self.NotifyAndGetResponse(basestation, {"action":"set","resource":"cameras/"+basestation.get('deviceId')+"/ambientSensors/config","publishResponse":True,"properties":{"airQuality":{"maxThreshold":number}}}) + + def SetAirQualityRecordingOn(self, basestation): + return self.NotifyAndGetResponse(basestation, {"action":"set","resource":"cameras/"+basestation.get('deviceId')+"/ambientSensors/config","publishResponse":True,"properties":{"airQuality":{"recordingEnabled":True}}}) + + def SetAirQualityRecordingOff(self, basestation): + return self.NotifyAndGetResponse(basestation, {"action":"set","resource":"cameras/"+basestation.get('deviceId')+"/ambientSensors/config","publishResponse":True,"properties":{"airQuality":{"recordingEnabled":False}}}) + + def SetHumidityAlertOn(self, basestation): + return self.NotifyAndGetResponse(basestation, {"action":"set","resource":"cameras/"+basestation.get('deviceId')+"/ambientSensors/config","publishResponse":True,"properties":{"humidity":{"alertsEnabled":True}}}) + + def SetHumidityAlertOff(self, basestation): + return self.NotifyAndGetResponse(basestation, {"action":"set","resource":"cameras/"+basestation.get('deviceId')+"/ambientSensors/config","publishResponse":True,"properties":{"humidity":{"alertsEnabled":False}}}) + + def SetHumidityAlertThresholdMin(self, basestation, number=400): + return self.NotifyAndGetResponse(basestation, {"action":"set","resource":"cameras/"+basestation.get('deviceId')+"/ambientSensors/config","publishResponse":True,"properties":{"humidity":{"minThreshold":number}}}) + + def SetHumidityAlertThresholdMax(self, basestation, number=800): + return self.NotifyAndGetResponse(basestation, {"action":"set","resource":"cameras/"+basestation.get('deviceId')+"/ambientSensors/config","publishResponse":True,"properties":{"humidity":{"maxThreshold":number}}}) + + def SetHumidityRecordingOn(self, basestation): + return self.NotifyAndGetResponse(basestation, {"action":"set","resource":"cameras/"+basestation.get('deviceId')+"/ambientSensors/config","publishResponse":True,"properties":{"humidity":{"recordingEnabled":True}}}) + + def SetHumidityRecordingOff(self, basestation): + return self.NotifyAndGetResponse(basestation, {"action":"set","resource":"cameras/"+basestation.get('deviceId')+"/ambientSensors/config","publishResponse":True,"properties":{"humidity":{"recordingEnabled":False}}}) + + def SetTempAlertOn(self, basestation): + return self.NotifyAndGetResponse(basestation, {"action":"set","resource":"cameras/"+basestation.get('deviceId')+"/ambientSensors/config","publishResponse":True,"properties":{"temperature":{"alertsEnabled":True}}}) + + def SetTempAlertOff(self, basestation): + return self.NotifyAndGetResponse(basestation, {"action":"set","resource":"cameras/"+basestation.get('deviceId')+"/ambientSensors/config","publishResponse":True,"properties":{"temperature":{"alertsEnabled":False}}}) + + def SetTempAlertThresholdMin(self, basestation, number=200): + return self.NotifyAndGetResponse(basestation, {"action":"set","resource":"cameras/"+basestation.get('deviceId')+"/ambientSensors/config","publishResponse":True,"properties":{"temperature":{"minThreshold":number}}}) + + def SetTempAlertThresholdMax(self, basestation, number=240): + return self.NotifyAndGetResponse(basestation, {"action":"set","resource":"cameras/"+basestation.get('deviceId')+"/ambientSensors/config","publishResponse":True,"properties":{"temperature":{"maxThreshold":number}}}) + + def SetTempRecordingOn(self, basestation): + return self.NotifyAndGetResponse(basestation, {"action":"set","resource":"cameras/"+basestation.get('deviceId')+"/ambientSensors/config","publishResponse":True,"properties":{"temperature":{"recordingEnabled":True}}}) + + def SetTempRecordingOff(self, basestation): + return self.NotifyAndGetResponse(basestation, {"action":"set","resource":"cameras/"+basestation.get('deviceId')+"/ambientSensors/config","publishResponse":True,"properties":{"temperature":{"recordingEnabled":False}}}) + + def SetTempUnit(self, uniqueId, unit="C"): + return self.request.post('https://my.arlo.com/hmsweb/users/devices/'+uniqueId+'/tempUnit', {"tempUnit":unit}) + + def SirenOn(self, basestation): + return self.NotifyAndGetResponse(basestation, {"action":"set","resource":"siren","publishResponse":True,"properties":{"sirenState":"on","duration":300,"volume":8,"pattern":"alarm"}}) + + def SirenOff(self, basestation): + return self.NotifyAndGetResponse(basestation, {"action":"set","resource":"siren","publishResponse":True,"properties":{"sirenState":"off","duration":300,"volume":8,"pattern":"alarm"}}) + + def Reset(self): + return self.request.get('https://my.arlo.com/hmsweb/users/library/reset') + + def GetServiceLevelSettings(self): + return self.request.get('https://my.arlo.com/hmsweb/users/serviceLevel/settings') + + def GetServiceLevel(self): + return self.request.get('https://my.arlo.com/hmsweb/users/serviceLevel') + + def GetServiceLevelV2(self): + """ DEPRECATED: This API still works, but I don't see it being called in the web UI anymore. """ + return self.request.get('https://my.arlo.com/hmsweb/users/serviceLevel/v2') + + def GetServiceLevelV3(self): + """ DEPRECATED: This API still works, but I don't see it being called in the web UI anymore. """ + return self.request.get('https://my.arlo.com/hmsweb/users/serviceLevel/v3') + + def GetServiceLevelV4(self): + return self.request.get('https://my.arlo.com/hmsweb/users/serviceLevel/v4') + + def GetUpdateFeatures(self): + return self.request.get('https://my.arlo.com/hmsweb/users/devices/updateFeatures/feature') + + def GetPaymentBilling(self): + return self.request.get('https://my.arlo.com/hmsweb/users/payment/billing/'+self.user_id) + + def GetPaymentOffers(self): + """ DEPRECATED: This API still works, but I don't see it being called in the web UI anymore. """ + return self.request.get('https://my.arlo.com/hmsweb/users/payment/offers') + + def GetPaymentOffersV2(self): + """ DEPRECATED: This API still works, but I don't see it being called in the web UI anymore. """ + return self.request.get('https://my.arlo.com/hmsweb/users/payment/offers/v2') + + def GetPaymentOffersV3(self): + """ DEPRECATED: This API still works, but I don't see it being called in the web UI anymore. """ + return self.request.get('https://my.arlo.com/hmsweb/users/payment/offers/v3') + + def GetPaymentOffersV4(self): + return self.request.get('https://my.arlo.com/hmsweb/users/payment/offers/v4') + + def SetOCProfile(self, firstName, lastName, country='United States', language='en', spam_me=0): + return self.request.post('https://my.arlo.com/hmsweb/users/ocprofile', {"firstName":"Jeffrey","lastName":"Walter","country":country,"language":language,"mailProgram":spam_me}) + + def GetOCProfile(self): + return self.request.get('https://my.arlo.com/hmsweb/users/ocprofile') + + def GetProfile(self): + return self.request.get('https://my.arlo.com/hmsweb/users/profile') + + def GetSession(self): + """ + Returns something like the following: + { + "userId": "XXX-XXXXXXX", + "email": "jeffreydwalter@gmail.com", + "token": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + "paymentId": "XXXXXXXX", + "accountStatus": "registered", + "serialNumber": "XXXXXXXXXXXXXX", + "countryCode": "US", + "tocUpdate": false, + "policyUpdate": false, + "validEmail": true, + "arlo": true, + "dateCreated": 1463975008658 + } + """ + return self.request.get('https://my.arlo.com/hmsweb/users/session') + + def GetFriends(self): + return self.request.get('https://my.arlo.com/hmsweb/users/friends') + + def GetLocations(self): + """ + This call returns the following: + { + "id":"XXX-XXXXXXX_20160823042047", + "name":"Home", + "ownerId":"XXX-XXXXXXX", + "longitude":X.XXXXXXXXXXXXXXXX, + "latitude":X.XXXXXXXXXXXXXXXX, + "address":"123 Middle Of Nowhere Bumbfuck, EG, 12345", + "homeMode":"schedule", + "awayMode":"mode1", + "geoEnabled":false, + "geoRadius":150.0, + "uniqueIds":[ + "XXX-XXXXXXX_XXXXXXXXXXXXX" + ], + "smartDevices":[ + "XXXXXXXXXX", + "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" + ], + "pushNotifyDevices":[ + "XXXXXXXXXX" + ] + } + """ + return self.request.get('https://my.arlo.com/hmsweb/users/locations') + + def GetEmergencyLocations(self): + return self.request.get('https://my.arlo.com/hmsweb/users/emergency/locations') + + def Geofencing(self, location_id, active=True): + """ + Get location_id is the id field from the return of GetLocations() + NOTE: The Arlo API seems to disable geofencing mode when switching to other modes, if it's enabled. + You should probably do the same, although, the UI reflects the switch from calendar mode to say armed mode without explicitly setting calendar mode to inactive. + """ + return self.request.put('https://my.arlo.com/hmsweb/users/locations/'+location_id, {'geoEnabled':active}) + + def GetDevices(self, device_type=None, filter_provisioned=None): + """ + This method returns an array that contains the basestation, cameras, etc. and their metadata. + If you pass in a valid device type, as a string or a list, this method will return an array of just those devices that match that type. An example would be ['basestation', 'camera'] + To filter provisioned or unprovisioned devices pass in a True/False value for filter_provisioned. By default both types are returned. + """ + devices = self.request.get('https://my.arlo.com/hmsweb/users/devices') + if device_type: + devices = [ device for device in devices if device['deviceType'] in device_type] + + if filter_provisioned is not None: + if filter_provisioned: + devices = [ device for device in devices if device.get("state") == 'provisioned'] + else: + devices = [ device for device in devices if device.get("state") != 'provisioned'] + + return devices + + def GetDeviceSupport(self): + """ + DEPRECATED: This API still works, but I don't see it being called in the web UI anymore. + + This API looks like it's mainly used by the website, but I'm including it for completeness sake. + It returns something like the following: + { + "devices": [ + { + "deviceType": "arloq", + "urls": { + "troubleshoot": "https://vzs3-prod-common.s3.amazonaws.com/static/html/en/pc_troubleshoot.html", + "plugin": "https://vzs3-prod-common.s3.amazonaws.com/static/html/en/pc_plugin.html", + "connection": "https://vzs3-prod-common.s3.amazonaws.com/static/html/en/pc_connection.html", + "connectionFailed": "https://vzs3-prod-common.s3.amazonaws.com/static/html/en/pc_connection_fail.html", + "press_sync": "https://vzs3-prod-common.s3. amazonaws.com/static/html/en/pc_press_sync.html", + "resetDevice": "https://vzs3-prod-common.s3.amazonaws.com/static/html/en/reset_arloq.html", + "qr_how_to": "https://vzs3-prod-common.s3.amazonaws.com/static/html/en/pc_qr_how_to.html" + } + }, + { + "deviceType": "basestation", + "urls": { + "troubleshoot": "https://vzs3-prod-common.s3.amazonaws.com/static/html/en/bs_troubleshoot.html", + "connection": "https://vzs3-prod-common.s3.amazonaws.com/static/html/en/bs_connection.html", + "sync": "https://vzs3-prod-common.s3.amazonaws.com/static/html/en/bs_sync_camera.html" + } + }, + { + "deviceType": "arloqs", + "urls": { + "ethernetSetup": "https://vzs3-prod-common.s3.amazonaws.com/static/html/en/arloqs/ethernet_setup.html", + "plugin": "https:// vzs3-prod-common.s3.amazonaws.com/static/html/en/arloqs/aqp_plugin.html", + "connectionWiFi": "https://vzs3-prod-common.s3.amazonaws.com/static/html/en/arloqs/connection_in_progress_wifi.html", + "poeSetup": "https://vzs3-prod-common.s3. amazonaws.com/static/html/en/arloqs/poe_setup.html", + "connection": "https://vzs3-prod-common.s3.amazonaws.com/static/html/en/arloqs/connection_in_progress.html", + "connectionFailed": "https://vzs3-prod-common.s3.amazonaws.com/static/html/en/arloqs/connection_fail.html", + "press_sync": "https://vzs3-prod-common.s3.amazonaws.com/static/html/en/arloqs/press_sync.html", + "connectionType": "https://vzs3-prod-common.s3.amazonaws.com/static/html/en/arloqs/connection_type.html", + "resetDevice": "https://vzs3-prod-common.s3.amazonaws.com/static/html/en/arloqs/reset_device.html", + "qr_how_to": "https://vzs3-prod-common.s3.amazonaws.com/static/html/en/arloqs/qr_how_to.html" + } + } + ] + } + """ + return self.request.get('https://my.arlo.com/hmsweb/devicesupport') + + def GetDeviceSupportv2(self): + """ + DEPRECATED: This API still works, but I don't see it being called in the web UI anymore. + + It returns something like the following: + { + "devices": [ + { + "deviceType": "arloq", + "modelId": [ + "VMC3040" + ], + "urls": { + "troubleshoot": "arloq/troubleshoot.html", + "plugin": "arloq/plugin.html", + "qrHowTo": "arloq/qrHowTo.html", + "connection": "arloq/connection.html", + "connectionInProgress": "arloq/connectionInProgress.html", + "connectionFailed": "arloq/connectionFailed.html", + "pressSync": "arloq/pressSync.html", + "resetDevice": "arloq/resetDevice.html" + } + }, + { + "deviceType": "basestation", + "modelId": [ + "VMB3010", + "VMB3010r2", + "VMB3500", + "VMB4000", + "VMB4500", + "VZB3010" + ], + "urls": { + "troubleshoot": "basestation/troubleshoot.html", + "plugin": "basestation/plugin.html", + "sync3": "basestation/sync3.html", + "troubleshootBS": "basestation/troubleshootBS.html", + "connection": "basestation/connection.html", + "connectionInProgress": "basestation/connectionInProgress.html", + "sync2": "basestation/sync2.html", + "connectionFailed": "basestation/connectionFailed.html", + "sync1": "basestation/sync1.html", + "resetDevice": "basestation/resetDevice.html", + "syncComplete": "basestation/syncComplete.html" + } + }, + { + "deviceType": "arlobaby", + "modelId": [ + "ABC1000" + ], + "urls": { + "bleSetupError": "arlobaby/bleSetupError.html", + "troubleshoot": "arlobaby/troubleshoot.html", + "homekitCodeInstruction": "arlobaby/homekitCodeInstruction.html", + "connectionInProgress": "arlobaby/connectionInProgress.html", + "connectionFailed": "arlobaby/connectionFailed.html", + "resetDevice": "arlobaby/resetDevice.html", + "plugin": "arlobaby/plugin.html", + "qrHowTo": "arlobaby/qrHowTo.html", + "warning": "arlobaby/warning.html", + "connection": "arlobaby/connection.html", + "pressSync": "arlobaby/pressSync.html", + "bleInactive": "arlobaby/bleInactive.html", + "pluginIOS": "arlobaby/pluginIOS.html", + "homekitSetup": "arlobaby/homekitSetup.html" + } + }, + { + "deviceType": "lteCamera", + "modelId": [ + "VML4030" + ], + "urls": { + "troubleshoot": "lteCamera/troubleshoot.html", + "resetHowTo": "lteCamera/resetHowTo.html", + "plugin": "lteCamera/plugin.html", + "qrHowTo": "lteCamera/qrHowTo.html", + "connectionInProgress": "lteCamera/connectionInProgress.html", + "connectionFailed": "lteCamera/connectionFailed.html", + "resetDevice": "lteCamera/resetHowTo.html", + "resetComplete": "lteCamera/resetComplete.html", + "syncComplete": "lteCamera/syncComplete.html" + } + }, + { + "deviceType": "arloqs", + "modelId": [ + "VMC3040S" + ], + "urls": { + "ethernetSetup": "arloqs/ethernetSetup.html", + "troubleshoot": "arloqs/troubleshoot.html", + "plugin": "arloqs/plugin.html", + "poeSetup": "arloqs/poeSetup.html", + "connectionInProgressWiFi": "arloqs/connectionInProgressWifi.html", + "qrHowTo": "arloqs/qrHowTo.html", + "connectionInProgress": "arloqs/connectionInProgress.html", + "connectionFailed": "arloqs/connectionFailed.html", + "pressSync": "arloqs/pressSync.html", + "connectionType": "arloqs/connectionType.html", + "resetDevice": "arloqs/resetDevice.html" + } + }, + { + "deviceType": "bridge", + "modelId": [ + "ABB1000" + ], + "urls": { + "troubleshoot": "bridge/troubleshoot.html", + "fwUpdateInProgress": "bridge/fwUpdateInProgress.html", + "qrHowToUnplug": "bridge/qrHowToUnplug.html", + "fwUpdateDone": "bridge/fwUpdateDone.html", + "fwUpdateAvailable": "bridge/fwUpdateAvailable.html", + "needHelp": "https://www.arlo.com/en-us/support/#support_arlo_light", + "wifiError": "bridge/wifiError.html", + "bleAndroid": "bridge/bleInactiveAND.html", + "bleIOS": "bridge/bleInactiveIOS.html", + "connectionInProgress": "bridge/connectionInProgress.html", + "connectionFailed": "bridge/connectionFailed.html", + "manualPair": "bridge/manualPairing.html", + "resetDevice": "bridge/resetDevice.html", + "lowPower": "bridge/lowPowerZoneSetup.html", + "fwUpdateFailed": "bridge/fwUpdateFailed.html", + "fwUpdateCheckFailed": "bridge/fwUpdateCheckFailed.html", + "plugin": "bridge/plugin.html", + "qrHowTo": "bridge/qrHowTo.html", + "pressSync": "bridge/pressSync.html", + "pluginNoLED": "bridge/pluginNoLED.html", + "fwUpdateCheck": "bridge/fwUpdateCheck.html" + } + }, + { + "deviceType": "lights", + "modelId": [ + "AL1101" + ], + "urls": { + "troubleshoot": "lights/troubleshoot.html", + "needHelp": "https://kb.netgear.com/000053159/Light-discovery-failed.html", + "bleInactiveAND": "lights/bleInactiveAND.html", + "connectionInProgress": "lights/connectionInProgress.html", + "connectionFailed": "lights/connectionFailed.html", + "addBattery": "lights/addBattery.html", + "tutorial1": "lights/tutorial1.html", + "plugin": "lights/plugin.html", + "tutorial2": "lights/tutorial2.html", + "tutorial3": "lights/tutorial3.html", + "configurationInProgress": "lights/configurationInProgress.html", + "qrHowTo": "lights/qrHowTo.html", + "pressSync": "lights/pressSync.html", + "bleInactiveIOS": "lights/bleInactiveIOS.html", + "syncComplete": "lights/syncComplete.html" + } + }, + { + "deviceType": "routerM1", + "modelId": [ + "MR1100" + ], + "urls": { + "troubleshoot": "routerM1/troubleshoot.html", + "help": "routerM1/help.html", + "pairingFailed": "routerM1/pairingFailed.html", + "needHelp": "https://acupdates.netgear.com/help/redirect.aspx?url=m1arlo-kbb", + "plugin": "routerM1/plugin.html", + "pairing": "routerM1/pairing.html", + "connectionInProgress": "routerM1/connectionInProgress.html", + "sync2": "routerM1/sync2.html", + "connectionFailed": "routerM1/connectionFailed.html", + "sync1": "routerM1/sync1.html", + "sync": "routerM1/sync.html", + "syncComplete": "routerM1/syncComplete.html" + } + } + ], + "selectionUrls": { + "addDevice": "addDeviceBsRuAqAqpLteAbcMrBgLt.html", + "selectBasestation": "selectBsMr.html", + "deviceSelection": "deviceBsAqAqpLteAbcMrLtSelection.html", + "selectLights": "selectBgLt.html" + }, + "baseUrl": "https://vzs3-prod-common.s3.amazonaws.com/static/v2/html/en/" + } + """ + return self.request.get('https://my.arlo.com/hmsweb/devicesupport/v2') + + def GetDeviceSupportV3(self): + """ + This is the latest version of the device support api. + It returns something like the following: + { + "data": { + "devices": { + "camera": { + "modelIds": [ + "VMC3010", + "VMC3030", + "VMC4030", + "VMC4030P", + "VMC5040", + "VZC3010", + "VZC3030" + ], + "connectionTypes": { + "WPS": true, + "BLE": true + }, + "kbArticles": { + "insertBatteries": "https://kb.arlo.com/980150/Safety-Rules-for-Arlo-Wire-Free-Camera-Batteries", + "syncBasestation": "https://kb.arlo.com/987/How-do-I-set-up-and-sync-my-Arlo-Wire-Free-cameras", + "sync": "https://kb.arlo.com/987/How-do-I-set-up-and-sync-my-Arlo-Wire-Free-camera", + "firmwareUpdate": "https://kb.arlo.com/4736/How-do-I-update-my-Arlo-firmware-manually" + } + }, + "arloq": { + "modelIds": [ + "VMC3040", + "VMC3040S" + ], + "kbArticles": { + "power": "https://kb.arlo.com/1001944/How-do-I-set-up-Arlo-Q-on-iOS", + "qrCode": "https://kb.arlo.com/1001944/How-do-I-set-up-Arlo-Q-on-iOS", + "power_android": "https://kb.arlo.com/1002006/How-do-I-set-up-Arlo-Q-on-Android", + "qrCode_android": "https://kb.arlo.com/1002006/How-do-I-set-up-Arlo-Q-on-Android" + } + }, + "basestation": { + "modelIds": [ + "VMB3010", + "VMB4000", + "VMB3010r2", + "VMB3500", + "VZB3010", + "VMB4500", + "VMB5000" + ], + "smartHubs": [ + "VMB5000" + ], + "kbArticles": { + "pluginNetworkCable": "https://kb.arlo.com/1179139/How-do-I-connect-my-Arlo-or-Arlo-Pro-base-station-to-the-Internet", + "power": "https://kb.arlo.com/1179139/How-do-I-connect-my-Arlo-or-Arlo-Pro-base-station-to-the-Internet", + "led": "https://kb.arlo.com/1179139/How-do-I-connect-my-Arlo-or-Arlo-Pro-base-station-to-the-Internet", + "learnMore": "https://kb.arlo.com/000062124/How-do-I-record-4K-videos-to-a-microSD-card" + } + }, + "arlobaby": { + "modelIds": [ + "ABC1000" + ], + "kbArticles": { + "power": "https://kb.arlo.com/1282682/How-do-I-power-cycle-my-Arlo-Baby-camera", + "qrCode": "https://kb.arlo.com/1282700/How-do-I-set-up-my-Arlo-Baby-camera" + } + }, + "lteCamera":{ + "modelIds":[ + "VML4030" + ], + "kbArticles":{ + "servicePlan":"https://kb.arlo.com/1286865/What-Arlo-Mobile-service-plans-are-available", + "simActivation":"https://kb.arlo.com/1286865/What-Arlo-Mobile-service-plans-are-available", + "qrCode":"https://kb.arlo.com/1201822/How-do-I-set-up-my-Arlo-Go-camera" + } + }, + "bridge": { + "modelIds": [ + "ABB1000" + ], + "kbArticles": { + "power": "https://kb.arlo.com/000062047", + "sync": "https://kb.arlo.com/000062037", + "qrCode": "https://kb.arlo.com/000061886", + "factoryReset": "https://kb.arlo.com/000061837" + } + }, + "lights": { + "modelIds": [ + "AL1101" + ], + "kbArticles": { + "sync": "https://kb.arlo.com/000062005", + "insertBatteries": "https://kb.arlo.com/000061952", + "qrCode": "https://kb.arlo.com/000061886" + } + }, + "routerM1":{ + "modelIds":[ + "MR1100" + ], + "kbArticles":{ + "lookupFailed":"https://kb.arlo.com/1179130/Arlo-can-t-discover-my-base-station-during-installation-what-do-I-do" + } + }, + "chime": { + "modelIds": [ + "AC1001" + ], + "kbArticles": { + "ledNotBlinking":"https://kb.arlo.com/000061924", + "led":"https://kb.arlo.com/000061847", + "factoryReset":"https://kb.arlo.com/000061879", + "connectionFailed":"https://kb.arlo.com/000061880" + } + }, + "doorbell": { + "modelIds": [ + "AAD1001" + ], + "kbArticles": { + "led":"https://kb.arlo.com/000061847", + "factoryReset":"https://kb.arlo.com/000061842", + "pairCamera":"https://kb.arlo.com/000061897", + "existingChime":"https://kb.arlo.com/000061856", + "noWiring":"https://kb.arlo.com/000061859", + "connectionFailed":"https://kb.arlo.com/000061868", + "pairCameraFailed":"https://kb.arlo.com/000061893", + "testChimeFailed":"https://kb.arlo.com/000061944" + }, + "videos": { + "chimeType": "https://youtu.be/axytuF63VC0", + "wireDoorbell": "https://youtu.be/_5D2n3iPqW0", + "switchSetting": "https://youtu.be/BUmd4fik2RE" + }, + "arloVideos": { + "chimeType": "https://vzs3-prod-common.s3.amazonaws.com/static/devicesupport/Arlo_Audio_Doorbell_Chime.mp4", + "wireDoorbell": "https://vzs3-prod-common.s3.amazonaws.com/static/devicesupport/Arlo_Audio_Doorbell_Wired.mp4", + "switchSetting": "https://vzs3-prod-common.s3.amazonaws.com/static/devicesupport/Arlo_Audio_Doorbell_Switch.mp4" + } + } + }, + "arlosmart": { + "kbArticles": { + "e911": "https://www.arlo.com/en-us/landing/arlosmart/", + "callFriend": "https://www.arlo.com/en-us/landing/arlosmart/", + "4kAddOnPopup": "https://www.arlo.com/en-us/landing/arlosmart/", + "cloudRecording": "https://www.arlo.com/en-us/landing/arlosmart/", + "manageArloSmart": "https://kb.arlo.com/000062115", + "otherVideo": "https://kb.arlo.com/000062115", + "packageDetection": "https://kb.arlo.com/000062114", + "whereIsBasicSubscriptionGone": "https://kb.arlo.com/000062163" + } + } + }, + "success":true + } + """ + return self.request.get('https://my.arlo.com/hmsweb/devicesupport/v3') + + def GetDeviceCapabilities(self, device): + model = device.get('modelId').lower() + return self.request.get('https://my.arlo.com/resources/capabilities/'+model+'/'+model+'_'+device.get('interfaceVersion')+'.json', raw=True) + + def GetLibraryMetaData(self, from_date, to_date): + return self.request.post('https://my.arlo.com/hmsweb/users/library/metadata', {'dateFrom':from_date, 'dateTo':to_date}) + + def UpdateProfile(self, first_name, last_name): + return self.request.put('https://my.arlo.com/hmsweb/users/profile', {'firstName': first_name, 'lastName': last_name}) + + def UpdatePassword(self, password): + r = self.request.post('https://my.arlo.com/hmsweb/users/changePassword', {'currentPassword':self.password,'newPassword':password}) + self.password = password + return r + + def UpdateFriend(self, body): + """ + This is an example of the json you would pass in the body: + { + "firstName":"Some", + "lastName":"Body", + "devices":{ + "XXXXXXXXXXXXX":"Camera 1", + "XXXXXXXXXXXXX":"Camera 2 ", + "XXXXXXXXXXXXX":"Camera 3" + }, + "lastModified":1463977440911, + "adminUser":true, + "email":"user@example.com", + "id":"XXX-XXXXXXX" + } + """ + return self.request.put('https://my.arlo.com/hmsweb/users/friends', body) + + def RemoveFriend(self, email): + """ + Removes a person you've granted access to. + + email: email of user you want to revoke access from. + """ + return self.request.post('https://my.arlo.com/hmsweb/users/friends/remove', {"email":email}) + + def AddFriend(self, firstname, lastname, email, devices={}, admin=False): + """ + This API will send an email to a user and if they accept, will give them access to the devices you specify. + NOTE: XXX-XXXXXXX_XXXXXXXXXXXX is the uniqueId field in your device object. + + {adminUser:false,firstName:John,lastName:Doe,email:john.doe@example.com,devices:{XXX-XXXXXXX_XXXXXXXXXXXX:Camera1,XXX-XXXXXXX_XXXXXXXXXXXX:Camera2}} + """ + return self.request.post('https://my.arlo.com/hmsweb/users/friends', {"adminUser":admin,"firstName":firstname,"lastName":lastname,"email":email,"devices":devices}) + + def ResendFriendInvite(self, friend): + """ + This API will resend an invitation email to a user that you've AddFriend'd. You will need to get the friend object by calling GetFriend() because it includes a token that must be passed to this API. + friend: {"ownerId":"XXX-XXXXXXX","token":"really long string that you get from the GetFriends() API","firstName":"John","lastName":"Doe","devices":{"XXX-XXXXXXX_XXXXXXXXXXXX":"Camera1","XXX-XXXXXXX_XXXXXXXXXXXX":"Camera2"},"lastModified":1548470485419,"adminUser":false,"email":"john.doe@example.com"} + """ + return self.request.post('https://my.arlo.com/hmsweb/users/friends', friend) + + def UpdateDeviceName(self, device, name): + return self.request.put('https://my.arlo.com/hmsweb/users/devices/renameDevice', {'deviceId':device.get('deviceId'), 'deviceName':name, 'parentId':device.get('parentId')}) + + def UpdateDisplayOrder(self, body): + """ + This is an example of the json you would pass in the body to UpdateDisplayOrder() of your devices in the UI. + + XXXXXXXXXXXXX is the device id of each camera. You can get this from GetDevices(). + { + "devices":{ + "XXXXXXXXXXXXX":1, + "XXXXXXXXXXXXX":2, + "XXXXXXXXXXXXX":3 + } + } + """ + return self.request.post('https://my.arlo.com/hmsweb/users/devices/displayOrder', body) + + def GetLibrary(self, from_date, to_date): + """ + This call returns the following: + presignedContentUrl is a link to the actual video in Amazon AWS. + presignedThumbnailUrl is a link to the thumbnail .jpg of the actual video in Amazon AWS. + + [ + { + "mediaDurationSecond": 30, + "contentType": "video/mp4", + "name": "XXXXXXXXXXXXX", + "presignedContentUrl": "https://arlos3-prod-z2.s3.amazonaws.com/XXXXXXX_XXXX_XXXX_XXXX_XXXXXXXXXXXXX/XXX-XXXXXXX/XXXXXXXXXXXXX/recordings/XXXXXXXXXXXXX.mp4?AWSAccessKeyId=XXXXXXXXXXXXXXXXXXXX&Expires=1472968703&Signature=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + "lastModified": 1472881430181, + "localCreatedDate": XXXXXXXXXXXXX, + "presignedThumbnailUrl": "https://arlos3-prod-z2.s3.amazonaws.com/XXXXXXX_XXXX_XXXX_XXXX_XXXXXXXXXXXXX/XXX-XXXXXXX/XXXXXXXXXXXXX/recordings/XXXXXXXXXXXXX_thumb.jpg?AWSAccessKeyId=XXXXXXXXXXXXXXXXXXXX&Expires=1472968703&Signature=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + "reason": "motionRecord", + "deviceId": "XXXXXXXXXXXXX", + "createdBy": "XXXXXXXXXXXXX", + "createdDate": "20160903", + "timeZone": "America/Chicago", + "ownerId": "XXX-XXXXXXX", + "utcCreatedDate": XXXXXXXXXXXXX, + "currentState": "new", + "mediaDuration": "00:00:30" + } + ] + """ + return self.request.post('https://my.arlo.com/hmsweb/users/library', {'dateFrom':from_date, 'dateTo':to_date}) + + def DeleteRecording(self, recording): + """ + Delete a single video recording from Arlo. + All of the date info and device id you need to pass into this method are given in the results of the GetLibrary() call. + """ + return self.request.post('https://my.arlo.com/hmsweb/users/library/recycle', {'data':[{'createdDate':recording.get('createdDate'),'utcCreatedDate':recording.get('createdDate'),'deviceId':recording.get('deviceId')}]}) + + def BatchDeleteRecordings(self, recordings): + """ + Delete a batch of video recordings from Arlo. + + The GetLibrary() call response json can be passed directly to this method if you'd like to delete the same list of videos you queried for. + If you want to delete some other batch of videos, then you need to send an array of objects representing each video you want to delete. + + [ + { + "createdDate":"20160904", + "utcCreatedDate":1473010280395, + "deviceId":"XXXXXXXXXXXXX" + }, + { + "createdDate":"20160904", + "utcCreatedDate":1473010280395, + "deviceId":"XXXXXXXXXXXXX" + } + ] + """ + if recordings: + return self.request.post('https://my.arlo.com/hmsweb/users/library/recycle', {'data':recordings}) + + def GetRecording(self, url, chunk_size=4096): + """ Returns the whole video from the presignedContentUrl. """ + video = '' + r = requests.get(url, stream=True) + r.raise_for_status() + + for chunk in r.iter_content(chunk_size): + if chunk: video += chunk + return video + + def StreamRecording(self, url, chunk_size=4096): + """ + Returns a generator that is the chunked video stream from the presignedContentUrl. + + url: presignedContentUrl + """ + r = requests.get(url, stream=True) + r.raise_for_status() + for chunk in r.iter_content(chunk_size): + yield chunk + + def DownloadRecording(self, url, to): + """ + Writes a video to a given local file path. + + url: presignedContentUrl + to: path where the file should be written + """ + stream = self.StreamRecording(url) + with open(to, 'wb') as fd: + for chunk in stream: + fd.write(chunk) + fd.close() + + def DownloadSnapshot(self, url, to, chunk_size=4096): + """ + Writes a snapshot to a given local file path. + + url: presignedContentUrl or presignedFullFrameSnapshotUrl + to: path where the file should be written + """ + r = Request().get(url, stream=True) + with open(to, 'wb') as fd: + for chunk in r.iter_content(chunk_size): + fd.write(chunk) + fd.close() + + def StartStream(self, basestation, camera): + """ + This function returns the url of the rtsp video stream. + This stream needs to be called within 30 seconds or else it becomes invalid. + It can be streamed with: ffmpeg -re -i 'rtsps://' -acodec copy -vcodec copy test.mp4 + The request to /users/devices/startStream returns: { url:rtsp://:443/vzmodulelive?egressToken=b&userAgent=iOS&cameraId=} + """ + # nonlocal variable hack for Python 2.x. + class nl: + stream_url_dict = None + + def trigger(self): + nl.stream_url_dict = self.request.post('https://my.arlo.com/hmsweb/users/devices/startStream', {"to":camera.get('parentId'),"from":self.user_id+"_web","resource":"cameras/"+camera.get('deviceId'),"action":"set","responseUrl":"", "publishResponse":True,"transId":self.genTransId(),"properties":{"activityState":"startUserStream","cameraId":camera.get('deviceId')}}, headers={"xcloudId":camera.get('xCloudId')}) + + def callback(self, event): + if event.get("from") == basestation.get("deviceId") and event.get("resource") == "cameras/"+camera.get("deviceId") and event.get("properties", {}).get("activityState") == "userStreamActive": + return nl.stream_url_dict['url'].replace("rtsp://", "rtsps://") + + return None + + return self.TriggerAndHandleEvent(basestation, trigger, callback) + + def StopStream(self, basestation, camera): + + # nonlocal variable hack for Python 2.x. + class nl: + stream_url_dict = None + + def trigger(self): + self.request.post('https://my.arlo.com/hmsweb/users/devices/stopStream', {"to":camera.get('parentId'),"from":self.user_id+"_web","resource":"cameras/"+camera. get('deviceId'),"action":"set","responseUrl":"", "publishResponse":True,"transId":self.genTransId(),"properties":{"activityState":"stopUserStream","cameraId":camera.get('deviceId')}}, headers={"xcloudId": camera.get('xCloudId')}) + + def callback(self, event): + if event.get("from") == basestation.get("deviceId") and event.get("resource") == "cameras/"+camera.get("deviceId") and event.get("properties", {}).get("activityState") == "userStreamActive": + return nl.stream_url_dict['url'].replace("rtsp://", "rtsps://") + return None + + return self.TriggerAndHandleEvent(basestation, trigger, callback) + + def TriggerStreamSnapshot(self, basestation, camera): + """ + This function causes the camera to snapshot while recording. + NOTE: You MUST call StartStream() before calling this function. + If you call StartStream(), you have to start reading data from the stream, or streaming will be cancelled + and taking a snapshot may fail (since it requires the stream to be active). + + NOTE: You should not use this function is you just want a snapshot and aren't intending to stream. + Use TriggerFullFrameSnapshot() instead. + + NOTE: Use DownloadSnapshot() to download the actual image file. + """ + def trigger(self): + self.request.post('https://my.arlo.com/hmsweb/users/devices/takeSnapshot', {'xcloudId':camera.get('xCloudId'),'parentId':camera.get('parentId'),'deviceId':camera.get('deviceId'),'olsonTimeZone':camera.get('properties', {}).get('olsonTimeZone')}, headers={"xcloudId":camera.get('xCloudId')}) + + def callback(self, event): + if event.get("deviceId") == camera.get("deviceId") and event.get("resource") == "mediaUploadNotification": + presigned_content_url = event.get("presignedContentUrl") + if presigned_content_url is not None: + return presigned_content_url + + return None + + return self.TriggerAndHandleEvent(basestation, trigger, callback) + + def TriggerFullFrameSnapshot(self, basestation, camera): + """ + This function causes the camera to record a fullframe snapshot. + The presignedFullFrameSnapshotUrl url is returned. + Use DownloadSnapshot() to download the actual image file. + """ + def trigger(self): + self.request.post("https://my.arlo.com/hmsweb/users/devices/fullFrameSnapshot", {"to":camera.get("parentId"),"from":self.user_id+"_web","resource":"cameras/"+camera.get("deviceId"),"action":"set","publishResponse":True,"transId":self.genTransId(),"properties":{"activityState":"fullFrameSnapshot"}}, headers={"xcloudId":camera.get("xCloudId")}) + + def callback(self, event): + if event.get("from") == basestation.get("deviceId") and event.get("resource") == "cameras/"+camera.get("deviceId") and event.get("action") == "fullFrameSnapshotAvailable": + return event.get("properties", {}).get("presignedFullFrameSnapshotUrl") + return None + + return self.TriggerAndHandleEvent(basestation, trigger, callback) + + def StartRecording(self, basestation, camera): + """ + This function causes the camera to start recording. + You can get the timezone from GetDevices(). + """ + stream_url = self.StartStream(basestation, camera) + self.request.post('https://my.arlo.com/hmsweb/users/devices/startRecord', {'xcloudId':camera.get('xCloudId'),'parentId':camera.get('parentId'),'deviceId':camera.get('deviceId'),'olsonTimeZone':camera.get('properties', {}).get('olsonTimeZone')}, headers={"xcloudId":camera.get('xCloudId')}) + return stream_url + + def StopRecording(self, camera): + """ + This function causes the camera to stop recording. + You can get the timezone from GetDevices(). + """ + return self.request.post('https://my.arlo.com/hmsweb/users/devices/stopRecord', {'xcloudId':camera.get('xCloudId'),'parentId':camera.get('parentId'),'deviceId':camera.get('deviceId'),'olsonTimeZone':camera.get('properties', {}).get('olsonTimeZone')}, headers={"xcloudId":camera.get('xCloudId')}) + + def GetCvrPlaylist(self, camera, fromDate, toDate): + """ This function downloads a Cvr Playlist file for the period fromDate to toDate. """ + return self.request.get('https://my.arlo.com/hmsweb/users/devices/'+camera.get('deviceId')+'/playlist?fromDate='+fromDate+'&toDate='+toDate) diff --git a/RPI Code/Arlo/dev/html2text.py b/RPI Code/Arlo/dev/html2text.py new file mode 100644 index 0000000..1752890 --- /dev/null +++ b/RPI Code/Arlo/dev/html2text.py @@ -0,0 +1,914 @@ +#!/usr/bin/env python +"""html2text: Turn HTML into equivalent Markdown-structured text.""" +__version__ = "3.200.3" +__author__ = "Aaron Swartz (me@aaronsw.com)" +__copyright__ = "(C) 2004-2008 Aaron Swartz. GNU GPL 3." +__contributors__ = ["Martin 'Joey' Schulze", "Ricardo Reyes", "Kevin Jay North"] + +# TODO: +# Support decoded entities with unifiable. + +try: + True +except NameError: + setattr(__builtins__, 'True', 1) + setattr(__builtins__, 'False', 0) + +def has_key(x, y): + if hasattr(x, 'has_key'): return x.has_key(y) + else: return y in x + +try: + import htmlentitydefs + import urlparse + import HTMLParser +except ImportError: #Python3 + import html.entities as htmlentitydefs + import urllib.parse as urlparse + import html.parser as HTMLParser +try: #Python3 + import urllib.request as urllib +except: + import urllib +import optparse, re, sys, codecs, types + +try: from textwrap import wrap +except: pass + +# Use Unicode characters instead of their ascii psuedo-replacements +UNICODE_SNOB = 0 + +# Escape all special characters. Output is less readable, but avoids corner case formatting issues. +ESCAPE_SNOB = 0 + +# Put the links after each paragraph instead of at the end. +LINKS_EACH_PARAGRAPH = 0 + +# Wrap long lines at position. 0 for no wrapping. (Requires Python 2.3.) +BODY_WIDTH = 78 + +# Don't show internal links (href="#local-anchor") -- corresponding link targets +# won't be visible in the plain text file anyway. +SKIP_INTERNAL_LINKS = True + +# Use inline, rather than reference, formatting for images and links +INLINE_LINKS = True + +# Number of pixels Google indents nested lists +GOOGLE_LIST_INDENT = 36 + +IGNORE_ANCHORS = False +IGNORE_IMAGES = False +IGNORE_EMPHASIS = False + +### Entity Nonsense ### + +def name2cp(k): + if k == 'apos': return ord("'") + if hasattr(htmlentitydefs, "name2codepoint"): # requires Python 2.3 + return htmlentitydefs.name2codepoint[k] + else: + k = htmlentitydefs.entitydefs[k] + if k.startswith("&#") and k.endswith(";"): return int(k[2:-1]) # not in latin-1 + return ord(codecs.latin_1_decode(k)[0]) + +unifiable = {'rsquo':"'", 'lsquo':"'", 'rdquo':'"', 'ldquo':'"', +'copy':'(C)', 'mdash':'--', 'nbsp':' ', 'rarr':'->', 'larr':'<-', 'middot':'*', +'ndash':'-', 'oelig':'oe', 'aelig':'ae', +'agrave':'a', 'aacute':'a', 'acirc':'a', 'atilde':'a', 'auml':'a', 'aring':'a', +'egrave':'e', 'eacute':'e', 'ecirc':'e', 'euml':'e', +'igrave':'i', 'iacute':'i', 'icirc':'i', 'iuml':'i', +'ograve':'o', 'oacute':'o', 'ocirc':'o', 'otilde':'o', 'ouml':'o', +'ugrave':'u', 'uacute':'u', 'ucirc':'u', 'uuml':'u', +'lrm':'', 'rlm':''} + +unifiable_n = {} + +for k in unifiable.keys(): + unifiable_n[name2cp(k)] = unifiable[k] + +### End Entity Nonsense ### + +def onlywhite(line): + """Return true if the line does only consist of whitespace characters.""" + for c in line: + if c is not ' ' and c is not ' ': + return c is ' ' + return line + +def hn(tag): + if tag[0] == 'h' and len(tag) == 2: + try: + n = int(tag[1]) + if n in range(1, 10): return n + except ValueError: return 0 + +def dumb_property_dict(style): + """returns a hash of css attributes""" + return dict([(x.strip(), y.strip()) for x, y in [z.split(':', 1) for z in style.split(';') if ':' in z]]); + +def dumb_css_parser(data): + """returns a hash of css selectors, each of which contains a hash of css attributes""" + # remove @import sentences + data += ';' + importIndex = data.find('@import') + while importIndex != -1: + data = data[0:importIndex] + data[data.find(';', importIndex) + 1:] + importIndex = data.find('@import') + + # parse the css. reverted from dictionary compehension in order to support older pythons + elements = [x.split('{') for x in data.split('}') if '{' in x.strip()] + try: + elements = dict([(a.strip(), dumb_property_dict(b)) for a, b in elements]) + except ValueError: + elements = {} # not that important + + return elements + +def element_style(attrs, style_def, parent_style): + """returns a hash of the 'final' style attributes of the element""" + style = parent_style.copy() + if 'class' in attrs: + for css_class in attrs['class'].split(): + css_style = style_def['.' + css_class] + style.update(css_style) + if 'style' in attrs: + immediate_style = dumb_property_dict(attrs['style']) + style.update(immediate_style) + return style + +def google_list_style(style): + """finds out whether this is an ordered or unordered list""" + if 'list-style-type' in style: + list_style = style['list-style-type'] + if list_style in ['disc', 'circle', 'square', 'none']: + return 'ul' + return 'ol' + +def google_has_height(style): + """check if the style of the element has the 'height' attribute explicitly defined""" + if 'height' in style: + return True + return False + +def google_text_emphasis(style): + """return a list of all emphasis modifiers of the element""" + emphasis = [] + if 'text-decoration' in style: + emphasis.append(style['text-decoration']) + if 'font-style' in style: + emphasis.append(style['font-style']) + if 'font-weight' in style: + emphasis.append(style['font-weight']) + return emphasis + +def google_fixed_width_font(style): + """check if the css of the current element defines a fixed width font""" + font_family = '' + if 'font-family' in style: + font_family = style['font-family'] + if 'Courier New' == font_family or 'Consolas' == font_family: + return True + return False + +def list_numbering_start(attrs): + """extract numbering from list element attributes""" + if 'start' in attrs: + return int(attrs['start']) - 1 + else: + return 0 + +class HTML2Text(HTMLParser.HTMLParser): + def __init__(self, out=None, baseurl=''): + HTMLParser.HTMLParser.__init__(self) + + # Config options + self.unicode_snob = UNICODE_SNOB + self.escape_snob = ESCAPE_SNOB + self.links_each_paragraph = LINKS_EACH_PARAGRAPH + self.body_width = BODY_WIDTH + self.skip_internal_links = SKIP_INTERNAL_LINKS + self.inline_links = INLINE_LINKS + self.google_list_indent = GOOGLE_LIST_INDENT + self.ignore_links = IGNORE_ANCHORS + self.ignore_images = IGNORE_IMAGES + self.ignore_emphasis = IGNORE_EMPHASIS + self.google_doc = False + self.ul_item_mark = '*' + self.emphasis_mark = '_' + self.strong_mark = '**' + + if out is None: + self.out = self.outtextf + else: + self.out = out + + self.outtextlist = [] # empty list to store output characters before they are "joined" + + try: + self.outtext = unicode() + except NameError: # Python3 + self.outtext = str() + + self.quiet = 0 + self.p_p = 0 # number of newline character to print before next output + self.outcount = 0 + self.start = 1 + self.space = 0 + self.a = [] + self.astack = [] + self.maybe_automatic_link = None + self.absolute_url_matcher = re.compile(r'^[a-zA-Z+]+://') + self.acount = 0 + self.list = [] + self.blockquote = 0 + self.pre = 0 + self.startpre = 0 + self.code = False + self.br_toggle = '' + self.lastWasNL = 0 + self.lastWasList = False + self.style = 0 + self.style_def = {} + self.tag_stack = [] + self.emphasis = 0 + self.drop_white_space = 0 + self.inheader = False + self.abbr_title = None # current abbreviation definition + self.abbr_data = None # last inner HTML (for abbr being defined) + self.abbr_list = {} # stack of abbreviations to write later + self.baseurl = baseurl + + try: del unifiable_n[name2cp('nbsp')] + except KeyError: pass + unifiable['nbsp'] = ' _place_holder;' + + + def feed(self, data): + data = data.replace("", "") + HTMLParser.HTMLParser.feed(self, data) + + def handle(self, data): + self.feed(data) + self.feed("") + return self.optwrap(self.close()) + + def outtextf(self, s): + self.outtextlist.append(s) + if s: self.lastWasNL = s[-1] == '\n' + + def close(self): + HTMLParser.HTMLParser.close(self) + + self.pbr() + self.o('', 0, 'end') + + self.outtext = self.outtext.join(self.outtextlist) + if self.unicode_snob: + nbsp = unichr(name2cp('nbsp')) + else: + nbsp = u' ' + self.outtext = self.outtext.replace(u' _place_holder;', nbsp) + + return self.outtext + + def handle_charref(self, c): + self.o(self.charref(c), 1) + + def handle_entityref(self, c): + self.o(self.entityref(c), 1) + + def handle_starttag(self, tag, attrs): + self.handle_tag(tag, attrs, 1) + + def handle_endtag(self, tag): + self.handle_tag(tag, None, 0) + + def previousIndex(self, attrs): + """ returns the index of certain set of attributes (of a link) in the + self.a list + + If the set of attributes is not found, returns None + """ + if not has_key(attrs, 'href'): return None + + i = -1 + for a in self.a: + i += 1 + match = 0 + + if has_key(a, 'href') and a['href'] == attrs['href']: + if has_key(a, 'title') or has_key(attrs, 'title'): + if (has_key(a, 'title') and has_key(attrs, 'title') and + a['title'] == attrs['title']): + match = True + else: + match = True + + if match: return i + + def drop_last(self, nLetters): + if not self.quiet: + self.outtext = self.outtext[:-nLetters] + + def handle_emphasis(self, start, tag_style, parent_style): + """handles various text emphases""" + tag_emphasis = google_text_emphasis(tag_style) + parent_emphasis = google_text_emphasis(parent_style) + + # handle Google's text emphasis + strikethrough = 'line-through' in tag_emphasis and self.hide_strikethrough + bold = 'bold' in tag_emphasis and not 'bold' in parent_emphasis + italic = 'italic' in tag_emphasis and not 'italic' in parent_emphasis + fixed = google_fixed_width_font(tag_style) and not \ + google_fixed_width_font(parent_style) and not self.pre + + if start: + # crossed-out text must be handled before other attributes + # in order not to output qualifiers unnecessarily + if bold or italic or fixed: + self.emphasis += 1 + if strikethrough: + self.quiet += 1 + if italic: + self.o(self.emphasis_mark) + self.drop_white_space += 1 + if bold: + self.o(self.strong_mark) + self.drop_white_space += 1 + if fixed: + self.o('`') + self.drop_white_space += 1 + self.code = True + else: + if bold or italic or fixed: + # there must not be whitespace before closing emphasis mark + self.emphasis -= 1 + self.space = 0 + self.outtext = self.outtext.rstrip() + if fixed: + if self.drop_white_space: + # empty emphasis, drop it + self.drop_last(1) + self.drop_white_space -= 1 + else: + self.o('`') + self.code = False + if bold: + if self.drop_white_space: + # empty emphasis, drop it + self.drop_last(2) + self.drop_white_space -= 1 + else: + self.o(self.strong_mark) + if italic: + if self.drop_white_space: + # empty emphasis, drop it + self.drop_last(1) + self.drop_white_space -= 1 + else: + self.o(self.emphasis_mark) + # space is only allowed after *all* emphasis marks + if (bold or italic) and not self.emphasis: + self.o(" ") + if strikethrough: + self.quiet -= 1 + + def handle_tag(self, tag, attrs, start): + #attrs = fixattrs(attrs) + if attrs is None: + attrs = {} + else: + attrs = dict(attrs) + + if self.google_doc: + # the attrs parameter is empty for a closing tag. in addition, we + # need the attributes of the parent nodes in order to get a + # complete style description for the current element. we assume + # that google docs export well formed html. + parent_style = {} + if start: + if self.tag_stack: + parent_style = self.tag_stack[-1][2] + tag_style = element_style(attrs, self.style_def, parent_style) + self.tag_stack.append((tag, attrs, tag_style)) + else: + dummy, attrs, tag_style = self.tag_stack.pop() + if self.tag_stack: + parent_style = self.tag_stack[-1][2] + + if hn(tag): + self.p() + if start: + self.inheader = True + self.o(hn(tag)*"#" + ' ') + else: + self.inheader = False + return # prevent redundant emphasis marks on headers + + if tag in ['p', 'div']: + if self.google_doc: + if start and google_has_height(tag_style): + self.p() + else: + self.soft_br() + else: + self.p() + + if tag == "br" and start: self.o(" \n") + + if tag == "hr" and start: + self.p() + self.o("* * *") + self.p() + + if tag in ["head", "style", 'script']: + if start: self.quiet += 1 + else: self.quiet -= 1 + + if tag == "style": + if start: self.style += 1 + else: self.style -= 1 + + if tag in ["body"]: + self.quiet = 0 # sites like 9rules.com never close + + if tag == "blockquote": + if start: + self.p(); self.o('> ', 0, 1); self.start = 1 + self.blockquote += 1 + else: + self.blockquote -= 1 + self.p() + + if tag in ['em', 'i', 'u'] and not self.ignore_emphasis: self.o(self.emphasis_mark) + if tag in ['strong', 'b'] and not self.ignore_emphasis: self.o(self.strong_mark) + if tag in ['del', 'strike', 's']: + if start: + self.o("<"+tag+">") + else: + self.o("") + + if self.google_doc: + if not self.inheader: + # handle some font attributes, but leave headers clean + self.handle_emphasis(start, tag_style, parent_style) + + if tag in ["code", "tt"] and not self.pre: self.o('`') #TODO: `` `this` `` + if tag == "abbr": + if start: + self.abbr_title = None + self.abbr_data = '' + if has_key(attrs, 'title'): + self.abbr_title = attrs['title'] + else: + if self.abbr_title != None: + self.abbr_list[self.abbr_data] = self.abbr_title + self.abbr_title = None + self.abbr_data = '' + + if tag == "a" and not self.ignore_links: + if start: + if has_key(attrs, 'href') and not (self.skip_internal_links and attrs['href'].startswith('#')): + self.astack.append(attrs) + self.maybe_automatic_link = attrs['href'] + else: + self.astack.append(None) + else: + if self.astack: + a = self.astack.pop() + if self.maybe_automatic_link: + self.maybe_automatic_link = None + elif a: + if self.inline_links: + self.o("](" + escape_md(a['href']) + ")") + else: + i = self.previousIndex(a) + if i is not None: + a = self.a[i] + else: + self.acount += 1 + a['count'] = self.acount + a['outcount'] = self.outcount + self.a.append(a) + self.o("][" + str(a['count']) + "]") + + if tag == "img" and start and not self.ignore_images: + if has_key(attrs, 'src'): + attrs['href'] = attrs['src'] + alt = attrs.get('alt', '') + self.o("![" + escape_md(alt) + "]") + + if self.inline_links: + self.o("(" + escape_md(attrs['href']) + ")") + else: + i = self.previousIndex(attrs) + if i is not None: + attrs = self.a[i] + else: + self.acount += 1 + attrs['count'] = self.acount + attrs['outcount'] = self.outcount + self.a.append(attrs) + self.o("[" + str(attrs['count']) + "]") + + if tag == 'dl' and start: self.p() + if tag == 'dt' and not start: self.pbr() + if tag == 'dd' and start: self.o(' ') + if tag == 'dd' and not start: self.pbr() + + if tag in ["ol", "ul"]: + # Google Docs create sub lists as top level lists + if (not self.list) and (not self.lastWasList): + self.p() + if start: + if self.google_doc: + list_style = google_list_style(tag_style) + else: + list_style = tag + numbering_start = list_numbering_start(attrs) + self.list.append({'name':list_style, 'num':numbering_start}) + else: + if self.list: self.list.pop() + self.lastWasList = True + else: + self.lastWasList = False + + if tag == 'li': + self.pbr() + if start: + if self.list: li = self.list[-1] + else: li = {'name':'ul', 'num':0} + if self.google_doc: + nest_count = self.google_nest_count(tag_style) + else: + nest_count = len(self.list) + self.o(" " * nest_count) #TODO: line up
  1. s > 9 correctly. + if li['name'] == "ul": self.o(self.ul_item_mark + " ") + elif li['name'] == "ol": + li['num'] += 1 + self.o(str(li['num'])+". ") + self.start = 1 + + if tag in ["table", "tr"] and start: self.p() + if tag == 'td': self.pbr() + + if tag == "pre": + if start: + self.startpre = 1 + self.pre = 1 + else: + self.pre = 0 + self.p() + + def pbr(self): + if self.p_p == 0: + self.p_p = 1 + + def p(self): + self.p_p = 2 + + def soft_br(self): + self.pbr() + self.br_toggle = ' ' + + def o(self, data, puredata=0, force=0): + if self.abbr_data is not None: + self.abbr_data += data + + if not self.quiet: + if self.google_doc: + # prevent white space immediately after 'begin emphasis' marks ('**' and '_') + lstripped_data = data.lstrip() + if self.drop_white_space and not (self.pre or self.code): + data = lstripped_data + if lstripped_data != '': + self.drop_white_space = 0 + + if puredata and not self.pre: + data = re.sub('\s+', ' ', data) + if data and data[0] == ' ': + self.space = 1 + data = data[1:] + if not data and not force: return + + if self.startpre: + #self.out(" :") #TODO: not output when already one there + if not data.startswith("\n"): #
    stuff...
    +                    data = "\n" + data
    +
    +            bq = (">" * self.blockquote)
    +            if not (force and data and data[0] == ">") and self.blockquote: bq += " "
    +
    +            if self.pre:
    +                if not self.list:
    +                    bq += "    "
    +                #else: list content is already partially indented
    +                for i in xrange(len(self.list)):
    +                    bq += "    "
    +                data = data.replace("\n", "\n"+bq)
    +
    +            if self.startpre:
    +                self.startpre = 0
    +                if self.list:
    +                    data = data.lstrip("\n") # use existing initial indentation
    +
    +            if self.start:
    +                self.space = 0
    +                self.p_p = 0
    +                self.start = 0
    +
    +            if force == 'end':
    +                # It's the end.
    +                self.p_p = 0
    +                self.out("\n")
    +                self.space = 0
    +
    +            if self.p_p:
    +                self.out((self.br_toggle+'\n'+bq)*self.p_p)
    +                self.space = 0
    +                self.br_toggle = ''
    +
    +            if self.space:
    +                if not self.lastWasNL: self.out(' ')
    +                self.space = 0
    +
    +            if self.a and ((self.p_p == 2 and self.links_each_paragraph) or force == "end"):
    +                if force == "end": self.out("\n")
    +
    +                newa = []
    +                for link in self.a:
    +                    if self.outcount > link['outcount']:
    +                        self.out("   ["+ str(link['count']) +"]: " + urlparse.urljoin(self.baseurl, link['href']))
    +                        if has_key(link, 'title'): self.out(" ("+link['title']+")")
    +                        self.out("\n")
    +                    else:
    +                        newa.append(link)
    +
    +                if self.a != newa: self.out("\n") # Don't need an extra line when nothing was done.
    +
    +                self.a = newa
    +
    +            if self.abbr_list and force == "end":
    +                for abbr, definition in self.abbr_list.items():
    +                    self.out("  *[" + abbr + "]: " + definition + "\n")
    +
    +            self.p_p = 0
    +            self.out(data)
    +            self.outcount += 1
    +
    +    def handle_data(self, data):
    +        if r'\/script>' in data: self.quiet -= 1
    +
    +        if self.style:
    +            self.style_def.update(dumb_css_parser(data))
    +
    +        if not self.maybe_automatic_link is None:
    +            href = self.maybe_automatic_link
    +            if href == data and self.absolute_url_matcher.match(href):
    +                self.o("<" + data + ">")
    +                return
    +            else:
    +                self.o("[")
    +                self.maybe_automatic_link = None
    +
    +        if not self.code and not self.pre:
    +            data = escape_md_section(data, snob=self.escape_snob)
    +        self.o(data, 1)
    +
    +    def unknown_decl(self, data): pass
    +
    +    def charref(self, name):
    +        if name[0] in ['x','X']:
    +            c = int(name[1:], 16)
    +        else:
    +            c = int(name)
    +
    +        if not self.unicode_snob and c in unifiable_n.keys():
    +            return unifiable_n[c]
    +        else:
    +            try:
    +                return unichr(c)
    +            except NameError: #Python3
    +                return chr(c)
    +
    +    def entityref(self, c):
    +        if not self.unicode_snob and c in unifiable.keys():
    +            return unifiable[c]
    +        else:
    +            try: name2cp(c)
    +            except KeyError: return "&" + c + ';'
    +            else:
    +                try:
    +                    return unichr(name2cp(c))
    +                except NameError: #Python3
    +                    return chr(name2cp(c))
    +
    +    def replaceEntities(self, s):
    +        s = s.group(1)
    +        if s[0] == "#":
    +            return self.charref(s[1:])
    +        else: return self.entityref(s)
    +
    +    r_unescape = re.compile(r"&(#?[xX]?(?:[0-9a-fA-F]+|\w{1,8}));")
    +    def unescape(self, s):
    +        return self.r_unescape.sub(self.replaceEntities, s)
    +
    +    def google_nest_count(self, style):
    +        """calculate the nesting count of google doc lists"""
    +        nest_count = 0
    +        if 'margin-left' in style:
    +            nest_count = int(style['margin-left'][:-2]) / self.google_list_indent
    +        return nest_count
    +
    +
    +    def optwrap(self, text):
    +        """Wrap all paragraphs in the provided text."""
    +        if not self.body_width:
    +            return text
    +
    +        assert wrap, "Requires Python 2.3."
    +        result = ''
    +        newlines = 0
    +        for para in text.split("\n"):
    +            if len(para) > 0:
    +                if not skipwrap(para):
    +                    result += "\n".join(wrap(para, self.body_width))
    +                    if para.endswith('  '):
    +                        result += "  \n"
    +                        newlines = 1
    +                    else:
    +                        result += "\n\n"
    +                        newlines = 2
    +                else:
    +                    if not onlywhite(para):
    +                        result += para + "\n"
    +                        newlines = 1
    +            else:
    +                if newlines < 2:
    +                    result += "\n"
    +                    newlines += 1
    +        return result
    +
    +ordered_list_matcher = re.compile(r'\d+\.\s')
    +unordered_list_matcher = re.compile(r'[-\*\+]\s')
    +md_chars_matcher = re.compile(r"([\\\[\]\(\)])")
    +md_chars_matcher_all = re.compile(r"([`\*_{}\[\]\(\)#!])")
    +md_dot_matcher = re.compile(r"""
    +    ^             # start of line
    +    (\s*\d+)      # optional whitespace and a number
    +    (\.)          # dot
    +    (?=\s)        # lookahead assert whitespace
    +    """, re.MULTILINE | re.VERBOSE)
    +md_plus_matcher = re.compile(r"""
    +    ^
    +    (\s*)
    +    (\+)
    +    (?=\s)
    +    """, flags=re.MULTILINE | re.VERBOSE)
    +md_dash_matcher = re.compile(r"""
    +    ^
    +    (\s*)
    +    (-)
    +    (?=\s|\-)     # followed by whitespace (bullet list, or spaced out hr)
    +                  # or another dash (header or hr)
    +    """, flags=re.MULTILINE | re.VERBOSE)
    +slash_chars = r'\`*_{}[]()#+-.!'
    +md_backslash_matcher = re.compile(r'''
    +    (\\)          # match one slash
    +    (?=[%s])      # followed by a char that requires escaping
    +    ''' % re.escape(slash_chars),
    +    flags=re.VERBOSE)
    +
    +def skipwrap(para):
    +    # If the text begins with four spaces or one tab, it's a code block; don't wrap
    +    if para[0:4] == '    ' or para[0] == '\t':
    +        return True
    +    # If the text begins with only two "--", possibly preceded by whitespace, that's
    +    # an emdash; so wrap.
    +    stripped = para.lstrip()
    +    if stripped[0:2] == "--" and len(stripped) > 2 and stripped[2] != "-":
    +        return False
    +    # I'm not sure what this is for; I thought it was to detect lists, but there's
    +    # a 
    -inside- case in one of the tests that also depends upon it. + if stripped[0:1] == '-' or stripped[0:1] == '*': + return True + # If the text begins with a single -, *, or +, followed by a space, or an integer, + # followed by a ., followed by a space (in either case optionally preceeded by + # whitespace), it's a list; don't wrap. + if ordered_list_matcher.match(stripped) or unordered_list_matcher.match(stripped): + return True + return False + +def wrapwrite(text): + text = text.encode('utf-8') + try: #Python3 + sys.stdout.buffer.write(text) + except AttributeError: + sys.stdout.write(text) + +def html2text(html, baseurl=''): + h = HTML2Text(baseurl=baseurl) + return h.handle(html) + +def unescape(s, unicode_snob=False): + h = HTML2Text() + h.unicode_snob = unicode_snob + return h.unescape(s) + +def escape_md(text): + """Escapes markdown-sensitive characters within other markdown constructs.""" + return md_chars_matcher.sub(r"\\\1", text) + +def escape_md_section(text, snob=False): + """Escapes markdown-sensitive characters across whole document sections.""" + text = md_backslash_matcher.sub(r"\\\1", text) + if snob: + text = md_chars_matcher_all.sub(r"\\\1", text) + text = md_dot_matcher.sub(r"\1\\\2", text) + text = md_plus_matcher.sub(r"\1\\\2", text) + text = md_dash_matcher.sub(r"\1\\\2", text) + return text + + +def main(): + baseurl = '' + + p = optparse.OptionParser('%prog [(filename|url) [encoding]]', + version='%prog ' + __version__) + p.add_option("--ignore-emphasis", dest="ignore_emphasis", action="store_true", + default=IGNORE_EMPHASIS, help="don't include any formatting for emphasis") + p.add_option("--ignore-links", dest="ignore_links", action="store_true", + default=IGNORE_ANCHORS, help="don't include any formatting for links") + p.add_option("--ignore-images", dest="ignore_images", action="store_true", + default=IGNORE_IMAGES, help="don't include any formatting for images") + p.add_option("-g", "--google-doc", action="store_true", dest="google_doc", + default=False, help="convert an html-exported Google Document") + p.add_option("-d", "--dash-unordered-list", action="store_true", dest="ul_style_dash", + default=False, help="use a dash rather than a star for unordered list items") + p.add_option("-e", "--asterisk-emphasis", action="store_true", dest="em_style_asterisk", + default=False, help="use an asterisk rather than an underscore for emphasized text") + p.add_option("-b", "--body-width", dest="body_width", action="store", type="int", + default=BODY_WIDTH, help="number of characters per output line, 0 for no wrap") + p.add_option("-i", "--google-list-indent", dest="list_indent", action="store", type="int", + default=GOOGLE_LIST_INDENT, help="number of pixels Google indents nested lists") + p.add_option("-s", "--hide-strikethrough", action="store_true", dest="hide_strikethrough", + default=False, help="hide strike-through text. only relevant when -g is specified as well") + p.add_option("--escape-all", action="store_true", dest="escape_snob", + default=False, help="Escape all special characters. Output is less readable, but avoids corner case formatting issues.") + (options, args) = p.parse_args() + + # process input + encoding = "utf-8" + if len(args) > 0: + file_ = args[0] + if len(args) == 2: + encoding = args[1] + if len(args) > 2: + p.error('Too many arguments') + + if file_.startswith('http://') or file_.startswith('https://'): + baseurl = file_ + j = urllib.urlopen(baseurl) + data = j.read() + if encoding is None: + try: + from feedparser import _getCharacterEncoding as enc + except ImportError: + enc = lambda x, y: ('utf-8', 1) + encoding = enc(j.headers, data)[0] + if encoding == 'us-ascii': + encoding = 'utf-8' + else: + data = open(file_, 'rb').read() + if encoding is None: + try: + from chardet import detect + except ImportError: + detect = lambda x: {'encoding': 'utf-8'} + encoding = detect(data)['encoding'] + else: + data = sys.stdin.read() + + data = data.decode(encoding) + h = HTML2Text(baseurl=baseurl) + # handle options + if options.ul_style_dash: h.ul_item_mark = '-' + if options.em_style_asterisk: + h.emphasis_mark = '*' + h.strong_mark = '__' + + h.body_width = options.body_width + h.list_indent = options.list_indent + h.ignore_emphasis = options.ignore_emphasis + h.ignore_links = options.ignore_links + h.ignore_images = options.ignore_images + h.google_doc = options.google_doc + h.hide_strikethrough = options.hide_strikethrough + h.escape_snob = options.escape_snob + + wrapwrite(h.handle(data)) + + +if __name__ == "__main__": + main() diff --git a/RPI Code/Arlo/dev/rev.py b/RPI Code/Arlo/dev/rev.py new file mode 100644 index 0000000..e887341 --- /dev/null +++ b/RPI Code/Arlo/dev/rev.py @@ -0,0 +1,23 @@ +import fileinput +import os +import re +import sys + +if(len(sys.argv) != 2 or not os.path.isfile(sys.argv[1])): + print("Usage: {0} ".format(os.path.basename(sys.argv[0]))) + sys.exit(1) + +pattern = re.compile("\s*version='([0-9.]+)',") +line = "" +maj = "" +min = "" +ver = "" + +for line in fileinput.FileInput(sys.argv[1], inplace=1): + m = pattern.match(line) + if m: + version = m.groups()[0] + maj, min, rev = version.split('.') + line = line.replace(version, "{0}.{1}.{2}".format(maj, min, int(rev)+1)) + + sys.stdout.write(line) diff --git a/RPI Code/Arlo/docs/README.md b/RPI Code/Arlo/docs/README.md new file mode 100644 index 0000000..c9d04b9 --- /dev/null +++ b/RPI Code/Arlo/docs/README.md @@ -0,0 +1,1160 @@ +# `arlo` module + +Copyright 2016 Jeffrey D. Walter + +Licensed under the Apache License, Version 2.0 (the "License"); you may not +use this file except in compliance with the License. You may obtain a copy of +the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS ISBASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +License for the specific language governing permissions and limitations under +the License. + +## Classes + +` class Arlo ` + + + +### Class variables + +`var TRANSID_PREFIX` + + + +### Methods + +` def __init__(self, username, password) ` + + + +Initialize self. See help(type(self)) for accurate signature. + +` def AddFriend(self, firstname, lastname, email, devices={}, admin=False) ` + + + +This API will send an email to a user and if they accept, will give them +access to the devices you specify. NOTE: XXX-XXXXXXX_XXXXXXXXXXXX is the +uniqueId field in your device object. + +{adminUser:false,firstName:John,lastName:Doe,email:john.doe@example.com,device +s:{XXX-XXXXXXX_XXXXXXXXXXXX:Camera1,XXX-XXXXXXX_XXXXXXXXXXXX:Camera2}} + +` def AdjustBrightness(self, basestation, camera, brightness=0) ` + + + +NOTE: Brightness is between -2 and 2 in increments of 1 (-2, -1, 0, 1, 2). +Setting it to an invalid value has no effect. + +Returns: { "action": "is", "from": "XXXXXXXXXXXXX", "properties": { +"brightness": -2 }, "resource": "cameras/XXXXXXXXXXXXX", "to": +"336-XXXXXXX_web", "transId": "web!XXXXXXXX.389518!1514956240683" } + +` def AlertNotificationMethods(self, basestation, action='disabled', +email=False, push=False) ` + + + +**`action`** : `disabled` `OR` `recordSnapshot` `OR` `recordVideo` + +` def Arm(self, device) ` + + +` def BatchDeleteRecordings(self, recordings) ` + + + +Delete a batch of video recordings from Arlo. + +The GetLibrary() call response json can be passed directly to this method if +you'd like to delete the same list of videos you queried for. If you want to +delete some other batch of videos, then you need to send an array of objects +representing each video you want to delete. + +[ { "createdDate":"20160904", "utcCreatedDate":1473010280395, +"deviceId":"XXXXXXXXXXXXX" }, { "createdDate":"20160904", +"utcCreatedDate":1473010280395, "deviceId":"XXXXXXXXXXXXX" } ] + +` def Calendar(self, basestation, active=True) ` + + + +DEPRECATED: This API appears to still do stuff, but I don't see it called in +the web UI anymore when switching the mode to a schedule. + +NOTE: The Arlo API seems to disable calendar mode when switching to other +modes, if it's enabled. You should probably do the same, although, the UI +reflects the switch from calendar mode to say armed mode without explicitly +setting calendar mode to inactive. + +` def CustomMode(self, device, mode, schedules=[]) ` + + + +device can be any object that has parentId == deviceId. i.e., not a camera + +` def DeleteMode(self, device, mode) ` + + + +device can be any object that has parentId == deviceId. i.e., not a camera + +` def DeleteRecording(self, recording) ` + + + +Delete a single video recording from Arlo. All of the date info and device id +you need to pass into this method are given in the results of the GetLibrary() +call. + +` def Disarm(self, device) ` + + +` def DownloadRecording(self, url, to) ` + + + +Writes a video to a given local file path. + +**`url`** : `presignedContentUrl` + +**`to`** : `path` `where` `the` `file` `should` `be` `written` + +` def DownloadSnapshot(self, url, to, chunk_size=4096) ` + + + +Writes a snapshot to a given local file path. + +**`url`** : `presignedContentUrl` or `presignedFullFrameSnapshotUrl` + +**`to`** : `path` `where` `the` `file` `should` `be` `written` + +` def Geofencing(self, location_id, active=True) ` + + + +Get location_id is the id field from the return of GetLocations() NOTE: The +Arlo API seems to disable geofencing mode when switching to other modes, if +it's enabled. You should probably do the same, although, the UI reflects the +switch from calendar mode to say armed mode without explicitly setting +calendar mode to inactive. + +` def GetAudioPlayback(self, basestation) ` + + +` def GetAutomationActivityZones(self, camera) ` + + +` def GetAutomationDefinitions(self) ` + + +` def GetBaseStationState(self, basestation) ` + + +` def GetCalendar(self, basestation) ` + + +` def GetCameraState(self, basestation) ` + + +` def GetCameraTempReading(self, basestation) ` + + +` def GetCvrPlaylist(self, camera, fromDate, toDate) ` + + + +This function downloads a Cvr Playlist file for the period fromDate to toDate. + +` def GetDeviceCapabilities(self, device) ` + + +` def GetDeviceSupport(self) ` + + + +DEPRECATED: This API still works, but I don't see it being called in the web +UI anymore. + +This API looks like it's mainly used by the website, but I'm including it for +completeness sake. It returns something like the following: { "devices": [ { +"deviceType": "arloq", "urls": { "troubleshoot": "https://vzs3-prod- +common.s3.amazonaws.com/static/html/en/pc_troubleshoot.html", "plugin": +"https://vzs3-prod-common.s3.amazonaws.com/static/html/en/pc_plugin.html", +"connection": "https://vzs3-prod- +common.s3.amazonaws.com/static/html/en/pc_connection.html", +"connectionFailed": "https://vzs3-prod- +common.s3.amazonaws.com/static/html/en/pc_connection_fail.html", "press_sync": +"https://vzs3-prod-common.s3. +amazonaws.com/static/html/en/pc_press_sync.html", "resetDevice": "https://vzs3 +-prod-common.s3.amazonaws.com/static/html/en/reset_arloq.html", "qr_how_to": +"https://vzs3-prod-common.s3.amazonaws.com/static/html/en/pc_qr_how_to.html" } +}, { "deviceType": "basestation", "urls": { "troubleshoot": "https://vzs3 +-prod-common.s3.amazonaws.com/static/html/en/bs_troubleshoot.html", +"connection": "https://vzs3-prod- +common.s3.amazonaws.com/static/html/en/bs_connection.html", "sync": +"https://vzs3-prod-common.s3.amazonaws.com/static/html/en/bs_sync_camera.html" +} }, { "deviceType": "arloqs", "urls": { "ethernetSetup": "https://vzs3-prod- +common.s3.amazonaws.com/static/html/en/arloqs/ethernet_setup.html", "plugin": +"https:// vzs3-prod- +common.s3.amazonaws.com/static/html/en/arloqs/aqp_plugin.html", +"connectionWiFi": "https://vzs3-prod-common.s3.amazonaws.com/static/html/en/ar +loqs/connection_in_progress_wifi.html", "poeSetup": "https://vzs3-prod- +common.s3. amazonaws.com/static/html/en/arloqs/poe_setup.html", "connection": +"https://vzs3-prod- +common.s3.amazonaws.com/static/html/en/arloqs/connection_in_progress.html", +"connectionFailed": "https://vzs3-prod- +common.s3.amazonaws.com/static/html/en/arloqs/connection_fail.html", +"press_sync": "https://vzs3-prod- +common.s3.amazonaws.com/static/html/en/arloqs/press_sync.html", +"connectionType": "https://vzs3-prod- +common.s3.amazonaws.com/static/html/en/arloqs/connection_type.html", +"resetDevice": "https://vzs3-prod- +common.s3.amazonaws.com/static/html/en/arloqs/reset_device.html", "qr_how_to": +"https://vzs3-prod- +common.s3.amazonaws.com/static/html/en/arloqs/qr_how_to.html" } } ] } + +` def GetDeviceSupportV3(self) ` + + + +This is the latest version of the device support api. It returns something +like the following: { "data": { "devices": { "camera": { "modelIds": [ +"VMC3010", "VMC3030", "VMC4030", "VMC4030P", "VMC5040", "VZC3010", "VZC3030" +], "connectionTypes": { "WPS": true, "BLE": true }, "kbArticles": { +"insertBatteries": "https://kb.arlo.com/980150/Safety-Rules-for-Arlo-Wire- +Free-Camera-Batteries", "syncBasestation": "https://kb.arlo.com/987/How-do-I +-set-up-and-sync-my-Arlo-Wire-Free-cameras", "sync": "https://kb.arlo.com/987 +/How-do-I-set-up-and-sync-my-Arlo-Wire-Free-camera", "firmwareUpdate": +"https://kb.arlo.com/4736/How-do-I-update-my-Arlo-firmware-manually" } }, +"arloq": { "modelIds": [ "VMC3040", "VMC3040S" ], "kbArticles": { "power": +"https://kb.arlo.com/1001944/How-do-I-set-up-Arlo-Q-on-iOS", "qrCode": +"https://kb.arlo.com/1001944/How-do-I-set-up-Arlo-Q-on-iOS", "power_android": +"https://kb.arlo.com/1002006/How-do-I-set-up-Arlo-Q-on-Android", +"qrCode_android": "https://kb.arlo.com/1002006/How-do-I-set-up-Arlo-Q-on- +Android" } }, "basestation": { "modelIds": [ "VMB3010", "VMB4000", +"VMB3010r2", "VMB3500", "VZB3010", "VMB4500", "VMB5000" ], "smartHubs": [ +"VMB5000" ], "kbArticles": { "pluginNetworkCable": +"https://kb.arlo.com/1179139/How-do-I-connect-my-Arlo-or-Arlo-Pro-base- +station-to-the-Internet", "power": "https://kb.arlo.com/1179139/How-do-I +-connect-my-Arlo-or-Arlo-Pro-base-station-to-the-Internet", "led": +"https://kb.arlo.com/1179139/How-do-I-connect-my-Arlo-or-Arlo-Pro-base- +station-to-the-Internet", "learnMore": "https://kb.arlo.com/000062124/How-do-I +-record-4K-videos-to-a-microSD-card" } }, "arlobaby": { "modelIds": [ +"ABC1000" ], "kbArticles": { "power": "https://kb.arlo.com/1282682/How-do-I +-power-cycle-my-Arlo-Baby-camera", "qrCode": "https://kb.arlo.com/1282700/How- +do-I-set-up-my-Arlo-Baby-camera" } }, "lteCamera":{ "modelIds":[ "VML4030" ], +"kbArticles":{ "servicePlan":"https://kb.arlo.com/1286865/What-Arlo-Mobile- +service-plans-are-available", "simActivation":"https://kb.arlo.com/1286865 +/What-Arlo-Mobile-service-plans-are-available", +"qrCode":"https://kb.arlo.com/1201822/How-do-I-set-up-my-Arlo-Go-camera" } }, +"bridge": { "modelIds": [ "ABB1000" ], "kbArticles": { "power": +"https://kb.arlo.com/000062047", "sync": "https://kb.arlo.com/000062037", +"qrCode": "https://kb.arlo.com/000061886", "factoryReset": +"https://kb.arlo.com/000061837" } }, "lights": { "modelIds": [ "AL1101" ], +"kbArticles": { "sync": "https://kb.arlo.com/000062005", "insertBatteries": +"https://kb.arlo.com/000061952", "qrCode": "https://kb.arlo.com/000061886" } +}, "routerM1":{ "modelIds":[ "MR1100" ], "kbArticles":{ +"lookupFailed":"https://kb.arlo.com/1179130/Arlo-can-t-discover-my-base- +station-during-installation-what-do-I-do" } }, "chime": { "modelIds": [ +"AC1001" ], "kbArticles": { "ledNotBlinking":"https://kb.arlo.com/000061924", +"led":"https://kb.arlo.com/000061847", +"factoryReset":"https://kb.arlo.com/000061879", +"connectionFailed":"https://kb.arlo.com/000061880" } }, "doorbell": { +"modelIds": [ "AAD1001" ], "kbArticles": { +"led":"https://kb.arlo.com/000061847", +"factoryReset":"https://kb.arlo.com/000061842", +"pairCamera":"https://kb.arlo.com/000061897", +"existingChime":"https://kb.arlo.com/000061856", +"noWiring":"https://kb.arlo.com/000061859", +"connectionFailed":"https://kb.arlo.com/000061868", +"pairCameraFailed":"https://kb.arlo.com/000061893", +"testChimeFailed":"https://kb.arlo.com/000061944" }, "videos": { "chimeType": +"https://youtu.be/axytuF63VC0", "wireDoorbell": +"https://youtu.be/_5D2n3iPqW0", "switchSetting": +"https://youtu.be/BUmd4fik2RE" }, "arloVideos": { "chimeType": "https://vzs3 +-prod- +common.s3.amazonaws.com/static/devicesupport/Arlo_Audio_Doorbell_Chime.mp4", +"wireDoorbell": "https://vzs3-prod- +common.s3.amazonaws.com/static/devicesupport/Arlo_Audio_Doorbell_Wired.mp4", +"switchSetting": "https://vzs3-prod- +common.s3.amazonaws.com/static/devicesupport/Arlo_Audio_Doorbell_Switch.mp4" } +} }, "arlosmart": { "kbArticles": { "e911": "https://www.arlo.com/en- +us/landing/arlosmart/", "callFriend": "https://www.arlo.com/en- +us/landing/arlosmart/", "4kAddOnPopup": "https://www.arlo.com/en- +us/landing/arlosmart/", "cloudRecording": "https://www.arlo.com/en- +us/landing/arlosmart/", "manageArloSmart": "https://kb.arlo.com/000062115", +"otherVideo": "https://kb.arlo.com/000062115", "packageDetection": +"https://kb.arlo.com/000062114", "whereIsBasicSubscriptionGone": +"https://kb.arlo.com/000062163" } } }, "success":true } + +` def GetDeviceSupportv2(self) ` + + + +DEPRECATED: This API still works, but I don't see it being called in the web +UI anymore. + +It returns something like the following: { "devices": [ { "deviceType": +"arloq", "modelId": [ "VMC3040" ], "urls": { "troubleshoot": +"arloq/troubleshoot.html", "plugin": "arloq/plugin.html", "qrHowTo": +"arloq/qrHowTo.html", "connection": "arloq/connection.html", +"connectionInProgress": "arloq/connectionInProgress.html", "connectionFailed": +"arloq/connectionFailed.html", "pressSync": "arloq/pressSync.html", +"resetDevice": "arloq/resetDevice.html" } }, { "deviceType": "basestation", +"modelId": [ "VMB3010", "VMB3010r2", "VMB3500", "VMB4000", "VMB4500", +"VZB3010" ], "urls": { "troubleshoot": "basestation/troubleshoot.html", +"plugin": "basestation/plugin.html", "sync3": "basestation/sync3.html", +"troubleshootBS": "basestation/troubleshootBS.html", "connection": +"basestation/connection.html", "connectionInProgress": +"basestation/connectionInProgress.html", "sync2": "basestation/sync2.html", +"connectionFailed": "basestation/connectionFailed.html", "sync1": +"basestation/sync1.html", "resetDevice": "basestation/resetDevice.html", +"syncComplete": "basestation/syncComplete.html" } }, { "deviceType": +"arlobaby", "modelId": [ "ABC1000" ], "urls": { "bleSetupError": +"arlobaby/bleSetupError.html", "troubleshoot": "arlobaby/troubleshoot.html", +"homekitCodeInstruction": "arlobaby/homekitCodeInstruction.html", +"connectionInProgress": "arlobaby/connectionInProgress.html", +"connectionFailed": "arlobaby/connectionFailed.html", "resetDevice": +"arlobaby/resetDevice.html", "plugin": "arlobaby/plugin.html", "qrHowTo": +"arlobaby/qrHowTo.html", "warning": "arlobaby/warning.html", "connection": +"arlobaby/connection.html", "pressSync": "arlobaby/pressSync.html", +"bleInactive": "arlobaby/bleInactive.html", "pluginIOS": +"arlobaby/pluginIOS.html", "homekitSetup": "arlobaby/homekitSetup.html" } }, { +"deviceType": "lteCamera", "modelId": [ "VML4030" ], "urls": { "troubleshoot": +"lteCamera/troubleshoot.html", "resetHowTo": "lteCamera/resetHowTo.html", +"plugin": "lteCamera/plugin.html", "qrHowTo": "lteCamera/qrHowTo.html", +"connectionInProgress": "lteCamera/connectionInProgress.html", +"connectionFailed": "lteCamera/connectionFailed.html", "resetDevice": +"lteCamera/resetHowTo.html", "resetComplete": "lteCamera/resetComplete.html", +"syncComplete": "lteCamera/syncComplete.html" } }, { "deviceType": "arloqs", +"modelId": [ "VMC3040S" ], "urls": { "ethernetSetup": +"arloqs/ethernetSetup.html", "troubleshoot": "arloqs/troubleshoot.html", +"plugin": "arloqs/plugin.html", "poeSetup": "arloqs/poeSetup.html", +"connectionInProgressWiFi": "arloqs/connectionInProgressWifi.html", "qrHowTo": +"arloqs/qrHowTo.html", "connectionInProgress": +"arloqs/connectionInProgress.html", "connectionFailed": +"arloqs/connectionFailed.html", "pressSync": "arloqs/pressSync.html", +"connectionType": "arloqs/connectionType.html", "resetDevice": +"arloqs/resetDevice.html" } }, { "deviceType": "bridge", "modelId": [ +"ABB1000" ], "urls": { "troubleshoot": "bridge/troubleshoot.html", +"fwUpdateInProgress": "bridge/fwUpdateInProgress.html", "qrHowToUnplug": +"bridge/qrHowToUnplug.html", "fwUpdateDone": "bridge/fwUpdateDone.html", +"fwUpdateAvailable": "bridge/fwUpdateAvailable.html", "needHelp": +"https://www.arlo.com/en-us/support/#support_arlo_light", "wifiError": +"bridge/wifiError.html", "bleAndroid": "bridge/bleInactiveAND.html", "bleIOS": +"bridge/bleInactiveIOS.html", "connectionInProgress": +"bridge/connectionInProgress.html", "connectionFailed": +"bridge/connectionFailed.html", "manualPair": "bridge/manualPairing.html", +"resetDevice": "bridge/resetDevice.html", "lowPower": +"bridge/lowPowerZoneSetup.html", "fwUpdateFailed": +"bridge/fwUpdateFailed.html", "fwUpdateCheckFailed": +"bridge/fwUpdateCheckFailed.html", "plugin": "bridge/plugin.html", "qrHowTo": +"bridge/qrHowTo.html", "pressSync": "bridge/pressSync.html", "pluginNoLED": +"bridge/pluginNoLED.html", "fwUpdateCheck": "bridge/fwUpdateCheck.html" } }, { +"deviceType": "lights", "modelId": [ "AL1101" ], "urls": { "troubleshoot": +"lights/troubleshoot.html", "needHelp": "https://kb.netgear.com/000053159 +/Light-discovery-failed.html", "bleInactiveAND": "lights/bleInactiveAND.html", +"connectionInProgress": "lights/connectionInProgress.html", +"connectionFailed": "lights/connectionFailed.html", "addBattery": +"lights/addBattery.html", "tutorial1": "lights/tutorial1.html", "plugin": +"lights/plugin.html", "tutorial2": "lights/tutorial2.html", "tutorial3": +"lights/tutorial3.html", "configurationInProgress": +"lights/configurationInProgress.html", "qrHowTo": "lights/qrHowTo.html", +"pressSync": "lights/pressSync.html", "bleInactiveIOS": +"lights/bleInactiveIOS.html", "syncComplete": "lights/syncComplete.html" } }, +{ "deviceType": "routerM1", "modelId": [ "MR1100" ], "urls": { "troubleshoot": +"routerM1/troubleshoot.html", "help": "routerM1/help.html", "pairingFailed": +"routerM1/pairingFailed.html", "needHelp": +"https://acupdates.netgear.com/help/redirect.aspx?url=m1arlo-kbb", "plugin": +"routerM1/plugin.html", "pairing": "routerM1/pairing.html", +"connectionInProgress": "routerM1/connectionInProgress.html", "sync2": +"routerM1/sync2.html", "connectionFailed": "routerM1/connectionFailed.html", +"sync1": "routerM1/sync1.html", "sync": "routerM1/sync.html", "syncComplete": +"routerM1/syncComplete.html" } } ], "selectionUrls": { "addDevice": +"addDeviceBsRuAqAqpLteAbcMrBgLt.html", "selectBasestation": "selectBsMr.html", +"deviceSelection": "deviceBsAqAqpLteAbcMrLtSelection.html", "selectLights": +"selectBgLt.html" }, "baseUrl": "https://vzs3-prod- +common.s3.amazonaws.com/static/v2/html/en/" } + +` def GetDevices(self, device_type=None, filter_provisioned=None) ` + + + +This method returns an array that contains the basestation, cameras, etc. and +their metadata. If you pass in a valid device type, as a string or a list, +this method will return an array of just those devices that match that type. +An example would be ['basestation', 'camera'] To filter provisioned or +unprovisioned devices pass in a True/False value for filter_provisioned. By +default both types are returned. + +` def GetEmergencyLocations(self) ` + + +` def GetFriends(self) ` + + +` def GetLibrary(self, from_date, to_date) ` + + + +This call returns the following: presignedContentUrl is a link to the actual +video in Amazon AWS. presignedThumbnailUrl is a link to the thumbnail .jpg of +the actual video in Amazon AWS. + +[ { "mediaDurationSecond": 30, "contentType": "video/mp4", "name": +"XXXXXXXXXXXXX", "presignedContentUrl": +"https://arlos3-prod-z2.s3.amazonaws.com/XXXXXXX_XXXX_XXXX_XXXX_XXXXXXXXXXXXX +/XXX-XXXXXXX/XXXXXXXXXXXXX/recordings/XXXXXXXXXXXXX.mp4?AWSAccessKeyId=XXXXXXX +XXXXXXXXXXXXX&Expires=1472968703&Signature=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XX", "lastModified": 1472881430181, "localCreatedDate": XXXXXXXXXXXXX, +"presignedThumbnailUrl": +"https://arlos3-prod-z2.s3.amazonaws.com/XXXXXXX_XXXX_XXXX_XXXX_XXXXXXXXXXXXX +/XXX-XXXXXXX/XXXXXXXXXXXXX/recordings/XXXXXXXXXXXXX_thumb.jpg?AWSAccessKeyId=X +XXXXXXXXXXXXXXXXXXX&Expires=1472968703&Signature=XXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXXXXXXX", "reason": "motionRecord", "deviceId": "XXXXXXXXXXXXX", "createdBy": +"XXXXXXXXXXXXX", "createdDate": "20160903", "timeZone": "America/Chicago", +"ownerId": "XXX-XXXXXXX", "utcCreatedDate": XXXXXXXXXXXXX, "currentState": +"new", "mediaDuration": "00:00:30" } ] + +` def GetLibraryMetaData(self, from_date, to_date) ` + + +` def GetLocations(self) ` + + + +This call returns the following: { "id":"XXX-XXXXXXX_20160823042047", +"name":"Home", "ownerId":"XXX-XXXXXXX", "longitude":X.XXXXXXXXXXXXXXXX, +"latitude":X.XXXXXXXXXXXXXXXX, "address":"123 Middle Of Nowhere Bumbfuck, EG, +12345", "homeMode":"schedule", "awayMode":"mode1", "geoEnabled":false, +"geoRadius":150.0, "uniqueIds":[ "XXX-XXXXXXX_XXXXXXXXXXXXX" ], +"smartDevices":[ "XXXXXXXXXX", "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" ], +"pushNotifyDevices":[ "XXXXXXXXXX" ] } + +` def GetModes(self, basestation) ` + + + +DEPRECATED: This is the older API for getting the "mode". It still works, but +GetModesV2 is the way the Arlo software does it these days. + +` def GetModesV2(self) ` + + + +This is the newer API for getting the "mode". This method also returns the +schedules. Set a non-schedule mode to be active: {"activeAutomations":[{"devic +eId":"XXXXXXXXXXXXX","timestamp":1532015622105,"activeModes":["mode1"],"active +Schedules":[]}]} Set a schedule to be active: {"activeAutomations":[{"deviceId +":"XXXXXXXXXXXXX","timestamp":1532015790139,"activeModes":[],"activeSchedules" +:["schedule.1"]}]} + +` def GetOCProfile(self) ` + + +` def GetPaymentBilling(self) ` + + +` def GetPaymentOffers(self) ` + + + +DEPRECATED: This API still works, but I don't see it being called in the web +UI anymore. + +` def GetPaymentOffersV2(self) ` + + + +DEPRECATED: This API still works, but I don't see it being called in the web +UI anymore. + +` def GetPaymentOffersV3(self) ` + + + +DEPRECATED: This API still works, but I don't see it being called in the web +UI anymore. + +` def GetPaymentOffersV4(self) ` + + +` def GetProfile(self) ` + + +` def GetRecording(self, url, chunk_size=4096) ` + + + +Returns the whole video from the presignedContentUrl. + +` def GetRules(self, basestation) ` + + +` def GetSensorConfig(self, basestation) ` + + +` def GetServiceLevel(self) ` + + +` def GetServiceLevelSettings(self) ` + + +` def GetServiceLevelV2(self) ` + + + +DEPRECATED: This API still works, but I don't see it being called in the web +UI anymore. + +` def GetServiceLevelV3(self) ` + + + +DEPRECATED: This API still works, but I don't see it being called in the web +UI anymore. + +` def GetServiceLevelV4(self) ` + + +` def GetSession(self) ` + + + +Returns something like the following: { "userId": "XXX-XXXXXXX", "email": +"jeffreydwalter@gmail.com", "token": +"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "paymentId": +"XXXXXXXX", "accountStatus": "registered", "serialNumber": "XXXXXXXXXXXXXX", +"countryCode": "US", "tocUpdate": false, "policyUpdate": false, "validEmail": +true, "arlo": true, "dateCreated": 1463975008658 } + +` def GetSmartAlerts(self, camera) ` + + +` def GetSmartFeatures(self) ` + + +` def GetUpdateFeatures(self) ` + + +` def HandleEvents(self, basestation, callback, timeout=120) ` + + + +Use this method to subscribe to the event stream and provide a callback that +will be called for event event received. This function will allow you to +potentially write a callback that can handle all of the events received from +the event stream. + +` def Login(self, username, password) ` + + + +This call returns the following: { "userId":"XXX-XXXXXXX", +"email":"user@example.com", "token":"2_5HicFJMXXXXX-S_7IuK2EqOUHXXXXXXXXXXX1CX +KWTThgU18Va_XXXXXX5S00hUafv3PV_if_Bl_rhiFsDHYwhxI3CxlVnR5f3q2XXXXXX- +Wnt9F7D82uN1f4cXXXXX-FMUsWF_6tMBqwn6DpzOaIB7ciJrnr2QJyKewbQouGM6", +"paymentId":"XXXXXXXX", "authenticated":1472961381, +"accountStatus":"registered", "serialNumber":"XXXXXXXXXXXXX", +"countryCode":"US", "tocUpdate":false, "policyUpdate":false, "validEmail":true +} + +` def Logout(self) ` + + +` def Notify(self, basestation, body) ` + + + +The following are examples of the json you would need to pass in the body of +the Notify() call to interact with Arlo: + +###### + +###### + +**`NOTE`** : `While` `you` `can` `call` `Notify`() `directly`, `responses` `from` `these` `notify` `calls` `are` `sent` `to` `the` `EventStream` (`see` `Subscribe`()), + + +and so it's better to use the Get/Set methods that are implemented using the +NotifyAndGetResponse() method. + +###### + +###### + +Set System Mode (Armed, Disarmed) - {"from":"XXX-XXXXXXX_web","to":"XXXXXXXXXX +XXX","action":"set","resource":"modes","transId":"web!XXXXXXXX.XXXXXXXXXXXXXXX +XXXXX","publishResponse":true,"properties":{"active":"mode0"}} Set System Mode +(Calendar) - {"from":"XXX-XXXXXXX_web","to":"XXXXXXXXXXXXX","action":"set","re +source":"schedule","transId":"web!XXXXXXXX.XXXXXXXXXXXXXXXXXXXX","publishRespo +nse":true,"properties":{"active":true}} Configure The Schedule (Calendar) - +{"from":"XXX-XXXXXXX_web","to":"XXXXXXXXXXXXX","action":"set","resource":"sche +dule","transId":"web!XXXXXXXX.XXXXXXXXXXXXXXXXXXXX","publishResponse":true,"pr +operties":{"schedule":[{"modeId":"mode0","startTime":0},{"modeId":"mode2","sta +rtTime":28800000},{"modeId":"mode0","startTime":64800000},{"modeId":"mode0","s +tartTime":86400000},{"modeId":"mode2","startTime":115200000},{"modeId":"mode0" +,"startTime":151200000},{"modeId":"mode0","startTime":172800000},{"modeId":"mo +de2","startTime":201600000},{"modeId":"mode0","startTime":237600000},{"modeId" +:"mode0","startTime":259200000},{"modeId":"mode2","startTime":288000000},{"mod +eId":"mode0","startTime":324000000},{"modeId":"mode0","startTime":345600000},{ +"modeId":"mode2","startTime":374400000},{"modeId":"mode0","startTime":41040000 +0},{"modeId":"mode0","startTime":432000000},{"modeId":"mode0","startTime":5184 +00000}]} Create Mode - {"from":"XXX-XXXXXXX_web","to":"XXXXXXXXXXXXX","action" +:"add","resource":"rules","transId":"web!XXXXXXXX.XXXXXXXXXXXXXXXXXXXX","publi +shResponse":true,"properties":{"name":"Record video on Camera 1 if Camera 1 +detects motion","id":"ruleNew","triggers":[{"type":"pirMotionActive","deviceId +":"XXXXXXXXXXXXX","sensitivity":80}],"actions":[{"deviceId":"XXXXXXXXXXXXX","t +ype":"recordVideo","stopCondition":{"type":"timeout","timeout":15}},{"type":"s +endEmailAlert","recipients":["**OWNER_EMAIL**"]},{"type":"pushNotification"}]} +} {"from":"XXX-XXXXXXX_web","to":"XXXXXXXXXXXXX","action":"add","resource":"mo +des","transId":"web!XXXXXXXX.XXXXXXXXXXXXXXXXXXXX","publishResponse":true,"pro +perties":{"name":"Test","rules":["rule3"]}} Delete Mode - {"from":"XXX-XXXXXXX +_web","to":"XXXXXXXXXXXXX","action":"delete","resource":"modes/mode3","transId +":"web!XXXXXXXX.XXXXXXXXXXXXXXXXXXXX","publishResponse":true} Camera Off - +{"from":"XXX-XXXXXXX_web","to":"XXXXXXXXXXXXX","action":"set","resource":"came +ras/XXXXXXXXXXXXX","transId":"web!XXXXXXXX.XXXXXXXXXXXXXXXXXXXX","publishRespo +nse":true,"properties":{"privacyActive":false}} Night Vision On - {"from +":"XXX-XXXXXXX_web","to":"XXXXXXXXXXXXX","action":"set","resource":"cameras/XX +XXXXXXXXXXX","transId":"web!XXXXXXXX.XXXXXXXXXXXXXXXXXXXX","publishResponse":t +rue,"properties":{"zoom":{"topleftx":0,"toplefty":0,"bottomrightx":1280,"botto +mrighty":720},"mirror":true,"flip":true,"nightVisionMode":1,"powerSaveMode":2} +} Motion Detection Test - {"from":"XXX-XXXXXXX_web","to":"XXXXXXXXXXXXX","acti +on":"set","resource":"cameras/XXXXXXXXXXXXX","transId":"web!XXXXXXXX.XXXXXXXXX +XXXXXXXXXXX","publishResponse":true,"properties":{"motionSetupModeEnabled":tru +e,"motionSetupModeSensitivity":80}} + +device_id = locations.data.uniqueIds + +System Properties: ("resource":"modes") active (string) - Mode Selection +(mode2 = All Motion On, mode1 = Armed, mode0 = Disarmed, etc.) + +System Properties: ("resource":"schedule") active (bool) - Mode Selection +(true = Calendar) + +Camera Properties: ("resource":"cameras/{id}") privacyActive (bool) - Camera +On/Off zoom (topleftx (int), toplefty (int), bottomrightx (int), bottomrighty +(int)) - Camera Zoom Level mirror (bool) - Mirror Image (left-to-right or +right-to-left) flip (bool) - Flip Image Vertically nightVisionMode (int) - +Night Mode Enabled/Disabled (1, 0) powerSaveMode (int) - PowerSaver Mode (3 = +Best Video, 2 = Optimized, 1 = Best Battery Life) motionSetupModeEnabled +(bool) - Motion Detection Setup Enabled/Disabled motionSetupModeSensitivity +(int 0-100) - Motion Detection Sensitivity + +` def NotifyAndGetResponse(self, basestation, body, timeout=120) ` + + +` def PauseTrack(self, basestation) ` + + +` def Ping(self, basestation) ` + + +` def PlayTrack(self, basestation, +track_id='2391d620-e491-4412-99f6-e9a40d6046ed', position=0) ` + + + +Defaulting to 'hugh little baby', which is a supplied track. I hope the ID is +the same for all. + +` def PushToTalk(self, camera) ` + + +` def RemoveFriend(self, email) ` + + + +Removes a person you've granted access to. + +email: email of user you want to revoke access from. + +` def ResendFriendInvite(self, friend) ` + + + +This API will resend an invitation email to a user that you've AddFriend'd. +You will need to get the friend object by calling GetFriend() because it +includes a token that must be passed to this API. + +**`friend`** : {`"ownerId"`:`"XXX`-`XXXXXXX"`,`"token"`:`"really` `long` `string` `that` `you` `get` `from` `the` `GetFriends`() `API"`,`"firstName"`:`"John"`,`"lastName"`:`"Doe"`,`"devices"`:{`"XXX`-`XXXXXXX_XXXXXXXXXXXX"`:`"Camera1"`,`"XXX`-`XXXXXXX_XXXXXXXXXXXX"`:`"Camera2"`},`"lastModified"`:`1548470485419`,`"adminUser"`:`false`,`"email"`:`"john.doe`@`example.com"`} + +` def Reset(self) ` + + +` def RestartBasestation(self, basestation) ` + + +` def SetAirQualityAlertOff(self, basestation) ` + + +` def SetAirQualityAlertOn(self, basestation) ` + + +` def SetAirQualityAlertThresholdMax(self, basestation, number=700) ` + + +` def SetAirQualityAlertThresholdMin(self, basestation, number=400) ` + + +` def SetAirQualityRecordingOff(self, basestation) ` + + +` def SetAirQualityRecordingOn(self, basestation) ` + + +` def SetAudioAlertsOff(self, basestation, sensitivity=3) ` + + +` def SetAudioAlertsOn(self, basestation, sensitivity=3) ` + + +` def SetAutomationActivityZones(self, camera, zone, coords, color) ` + + + +An activity zone is the area you draw in your video in the UI to tell Arlo +what part of the scene to "watch". This method takes 4 arguments. camera: the +camera you want to set an activity zone for. name: "Zone 1" - the name of your +activity zone. coords: [{"x":0.37946943483275664,"y":0.3790983606557377},{"x": +0.8685121107266436,"y":0.3790983606557377},{"x":0.8685121107266436,"y":1},{"x" +:0.37946943483275664,"y":1}] - these coordinates are the bonding box for the +activity zone. color: 45136 - the color for your bounding box. + +` def SetHumidityAlertOff(self, basestation) ` + + +` def SetHumidityAlertOn(self, basestation) ` + + +` def SetHumidityAlertThresholdMax(self, basestation, number=800) ` + + +` def SetHumidityAlertThresholdMin(self, basestation, number=400) ` + + +` def SetHumidityRecordingOff(self, basestation) ` + + +` def SetHumidityRecordingOn(self, basestation) ` + + +` def SetLoopBackModeContinuous(self, basestation) ` + + +` def SetLoopBackModeSingleTrack(self, basestation) ` + + +` def SetMotionAlertsOff(self, basestation, sensitivity=5) ` + + +` def SetMotionAlertsOn(self, basestation, sensitivity=5) ` + + +` def SetNightLightBrightness(self, basestation, level=200) ` + + +` def SetNightLightColor(self, basestation, red=255, green=255, blue=255) ` + + +` def SetNightLightMode(self, basestation, mode='rainbow') ` + + + +mode: rainbow or rgb. + +` def SetNightLightOff(self, basestation) ` + + +` def SetNightLightOn(self, basestation) ` + + +` def SetNightLightTimerOff(self, basestation, time=0, timediff=300) ` + + +` def SetNightLightTimerOn(self, basestation, time=1576435472, timediff=0) ` + + +` def SetOCProfile(self, firstName, lastName, country='United States', +language='en', spam_me=0) ` + + +` def SetSchedule(self, basestation, schedule) ` + + + +The following json is what was sent to the API when I edited my schedule. It +contains all of the data necessary to configure a whole week. It's a little +convoluted, but you can just play around with the scheduler in Chrome and +watch the schema that gets sent. + +{ "schedule": [ { "duration": 600, "startActions": { "disableModes": [ "mode0" +], "enableModes": [ "mode1" ] }, "days": [ "Mo", "Tu", "We", "Th", "Fr", "Sa", +"Su" ], "startTime": 0, "type": "weeklyAction", "endActions": { +"disableModes": [ "mode1" ], "enableModes": [ "mode0" ] } }, { "duration": +360, "startActions": { "disableModes": [ "mode0" ], "enableModes": [ "mode2" ] +}, "days": [ "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su" ], "startTime": 1080, +"type": "weeklyAction", "endActions": { "disableModes": [ "mode2" ], +"enableModes": [ "mode0" ] } }, { "duration": 480, "startActions": { +"disableModes": [ "mode0" ], "enableModes": [ "mode3" ] }, "days": [ "Tu" ], +"startTime": 600, "type": "weeklyAction", "endActions": { "disableModes": [ +"mode3" ], "enableModes": [ "mode0" ] } } ], "name": "", "id": "schedule.1", +"enabled": true } + +` def SetShuffleOff(self, basestation) ` + + +` def SetShuffleOn(self, basestation) ` + + +` def SetSleepTimerOff(self, basestation, time=0, timediff=300) ` + + +` def SetSleepTimerOn(self, basestation, time=1576435472, timediff=0) ` + + +` def SetTempAlertOff(self, basestation) ` + + +` def SetTempAlertOn(self, basestation) ` + + +` def SetTempAlertThresholdMax(self, basestation, number=240) ` + + +` def SetTempAlertThresholdMin(self, basestation, number=200) ` + + +` def SetTempRecordingOff(self, basestation) ` + + +` def SetTempRecordingOn(self, basestation) ` + + +` def SetTempUnit(self, uniqueId, unit='C') ` + + +` def SetVolume(self, basestation, mute=False, volume=50) ` + + +` def SirenOff(self, basestation) ` + + +` def SirenOn(self, basestation) ` + + +` def SkipTrack(self, basestation) ` + + +` def StartRecording(self, basestation, camera) ` + + + +This function causes the camera to start recording. You can get the timezone +from GetDevices(). + +` def StartStream(self, basestation, camera) ` + + + +This function returns the url of the rtsp video stream. This stream needs to +be called within 30 seconds or else it becomes invalid. It can be streamed +with: ffmpeg -re -i 'rtsps://' -acodec copy -vcodec copy test.mp4 The request +to /users/devices/startStream returns: { +url:rtsp://:443/vzmodulelive?egressToken=b&userAgent=iOS&cameraId=} + +` def StopRecording(self, camera) ` + + + +This function causes the camera to stop recording. You can get the timezone +from GetDevices(). + +` def StopStream(self, basestation, camera) ` + + +` def StreamRecording(self, url, chunk_size=4096) ` + + + +Returns a generator that is the chunked video stream from the +presignedContentUrl. + +**`url`** : `presignedContentUrl` + +` def Subscribe(self, basestation) ` + + + +Arlo uses the EventStream interface in the browser to do pub/sub style +messaging. Unfortunately, this appears to be the only way Arlo communicates +these messages. + +This function makes the initial GET request to /subscribe, which returns the +EventStream socket. Once we have that socket, the API requires a POST request +to /notify with the "subscriptionsresource. This call "registersthe device +(which should be the basestation) so that events will be sent to the +EventStream when subsequent calls to /notify are made. + +Since this interface is asynchronous, and this is a quick and dirty hack to +get this working, I'm using a thread to listen to the EventStream. This thread +puts events into a queue. Some polling is required (see +NotifyAndGetResponse()) because the event messages aren't guaranteed to be +delivered in any specific order, but I wanted to maintain a synchronous style +API. + +You generally shouldn't need to call Subscribe() directly, although I'm +leaving it "publicfor now. + +` def SubscribeToMotionEvents(self, basestation, callback, timeout=120) ` + + + +Use this method to subscribe to motion events. You must provide a callback +function which will get called once per motion event. + +The callback function should have the following signature: def callback(self, +event) + +This is an example of handling a specific event, in reality, you'd probably +want to write a callback for HandleEvents() that has a big switch statement in +it to handle all the various events Arlo produces. + +` def ToggleCamera(self, basestation, camera, active=True) ` + + + +active: True - Camera is off. active: False - Camera is on. + +` def TriggerAndHandleEvent(self, basestation, trigger, callback, timeout=120) +` + + + +Use this method to subscribe to the event stream and provide a callback that +will be called for event event received. This function will allow you to +potentially write a callback that can handle all of the events received from +the event stream. NOTE: Use this function if you need to run some code after +subscribing to the eventstream, but before your callback to handle the events +runs. + +` def TriggerFullFrameSnapshot(self, basestation, camera) ` + + + +This function causes the camera to record a fullframe snapshot. The +presignedFullFrameSnapshotUrl url is returned. Use DownloadSnapshot() to +download the actual image file. + +` def TriggerStreamSnapshot(self, basestation, camera) ` + + + +This function causes the camera to snapshot while recording. NOTE: You MUST +call StartStream() before calling this function. If you call StartStream(), +you have to start reading data from the stream, or streaming will be cancelled +and taking a snapshot may fail (since it requires the stream to be active). + +NOTE: You should not use this function is you just want a snapshot and aren't +intending to stream. Use TriggerFullFrameSnapshot() instead. + +NOTE: Use DownloadSnapshot() to download the actual image file. + +` def UnPauseTrack(self, basestation) ` + + +` def Unsubscribe(self, basestation) ` + + + +This method stops the EventStream subscription and removes it from the +event_stream collection. + +` def UpdateDeviceName(self, device, name) ` + + +` def UpdateDisplayOrder(self, body) ` + + + +This is an example of the json you would pass in the body to +UpdateDisplayOrder() of your devices in the UI. + +XXXXXXXXXXXXX is the device id of each camera. You can get this from +GetDevices(). { "devices":{ "XXXXXXXXXXXXX":1, "XXXXXXXXXXXXX":2, +"XXXXXXXXXXXXX":3 } } + +` def UpdateFriend(self, body) ` + + + +This is an example of the json you would pass in the body: { +"firstName":"Some", "lastName":"Body", "devices":{ "XXXXXXXXXXXXX":"Camera 1", +"XXXXXXXXXXXXX":"Camera 2 ", "XXXXXXXXXXXXX":"Camera 3" }, +"lastModified":1463977440911, "adminUser":true, "email":"user@example.com", +"id":"XXX-XXXXXXX" } + +` def UpdatePassword(self, password) ` + + +` def UpdateProfile(self, first_name, last_name) ` + + +` def genTransId(self, trans_type='web') ` + + +` def interrupt_handler(self, signum, frame) ` + + +` def to_timestamp(self, dt) ` + + + +# Index + + * ### Classes + + * #### `Arlo` + + * `AddFriend` + * `AdjustBrightness` + * `AlertNotificationMethods` + * `Arm` + * `BatchDeleteRecordings` + * `Calendar` + * `CustomMode` + * `DeleteMode` + * `DeleteRecording` + * `Disarm` + * `DownloadRecording` + * `DownloadSnapshot` + * `Geofencing` + * `GetAudioPlayback` + * `GetAutomationActivityZones` + * `GetAutomationDefinitions` + * `GetBaseStationState` + * `GetCalendar` + * `GetCameraState` + * `GetCameraTempReading` + * `GetCvrPlaylist` + * `GetDeviceCapabilities` + * `GetDeviceSupport` + * `GetDeviceSupportV3` + * `GetDeviceSupportv2` + * `GetDevices` + * `GetEmergencyLocations` + * `GetFriends` + * `GetLibrary` + * `GetLibraryMetaData` + * `GetLocations` + * `GetModes` + * `GetModesV2` + * `GetOCProfile` + * `GetPaymentBilling` + * `GetPaymentOffers` + * `GetPaymentOffersV2` + * `GetPaymentOffersV3` + * `GetPaymentOffersV4` + * `GetProfile` + * `GetRecording` + * `GetRules` + * `GetSensorConfig` + * `GetServiceLevel` + * `GetServiceLevelSettings` + * `GetServiceLevelV2` + * `GetServiceLevelV3` + * `GetServiceLevelV4` + * `GetSession` + * `GetSmartAlerts` + * `GetSmartFeatures` + * `GetUpdateFeatures` + * `HandleEvents` + * `Login` + * `Logout` + * `Notify` + * `NotifyAndGetResponse` + * `PauseTrack` + * `Ping` + * `PlayTrack` + * `PushToTalk` + * `RemoveFriend` + * `ResendFriendInvite` + * `Reset` + * `RestartBasestation` + * `SetAirQualityAlertOff` + * `SetAirQualityAlertOn` + * `SetAirQualityAlertThresholdMax` + * `SetAirQualityAlertThresholdMin` + * `SetAirQualityRecordingOff` + * `SetAirQualityRecordingOn` + * `SetAudioAlertsOff` + * `SetAudioAlertsOn` + * `SetAutomationActivityZones` + * `SetHumidityAlertOff` + * `SetHumidityAlertOn` + * `SetHumidityAlertThresholdMax` + * `SetHumidityAlertThresholdMin` + * `SetHumidityRecordingOff` + * `SetHumidityRecordingOn` + * `SetLoopBackModeContinuous` + * `SetLoopBackModeSingleTrack` + * `SetMotionAlertsOff` + * `SetMotionAlertsOn` + * `SetNightLightBrightness` + * `SetNightLightColor` + * `SetNightLightMode` + * `SetNightLightOff` + * `SetNightLightOn` + * `SetNightLightTimerOff` + * `SetNightLightTimerOn` + * `SetOCProfile` + * `SetSchedule` + * `SetShuffleOff` + * `SetShuffleOn` + * `SetSleepTimerOff` + * `SetSleepTimerOn` + * `SetTempAlertOff` + * `SetTempAlertOn` + * `SetTempAlertThresholdMax` + * `SetTempAlertThresholdMin` + * `SetTempRecordingOff` + * `SetTempRecordingOn` + * `SetTempUnit` + * `SetVolume` + * `SirenOff` + * `SirenOn` + * `SkipTrack` + * `StartRecording` + * `StartStream` + * `StopRecording` + * `StopStream` + * `StreamRecording` + * `Subscribe` + * `SubscribeToMotionEvents` + * `TRANSID_PREFIX` + * `ToggleCamera` + * `TriggerAndHandleEvent` + * `TriggerFullFrameSnapshot` + * `TriggerStreamSnapshot` + * `UnPauseTrack` + * `Unsubscribe` + * `UpdateDeviceName` + * `UpdateDisplayOrder` + * `UpdateFriend` + * `UpdatePassword` + * `UpdateProfile` + * `__init__` + * `genTransId` + * `interrupt_handler` + * `to_timestamp` + +Generated by [pdoc 0.5.1](https://pdoc3.github.io/pdoc). + diff --git a/RPI Code/Arlo/docs/arlo.html b/RPI Code/Arlo/docs/arlo.html new file mode 100644 index 0000000..77bf710 --- /dev/null +++ b/RPI Code/Arlo/docs/arlo.html @@ -0,0 +1,1690 @@ + + + + + + +arlo API documentation + + + + + + + + +
    +
    +
    +

    arlo module

    +
    +
    +

    Copyright 2016 Jeffrey D. Walter

    +

    Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at

    +

    http://www.apache.org/licenses/LICENSE-2.0

    +

    Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS ISBASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License.

    +
    +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +class Arlo +
    +
    +
    +

    Class variables

    +
    +
    var TRANSID_PREFIX
    +
    +
    +
    +
    +

    Methods

    +
    +
    +def __init__(self, username, password) +
    +
    +

    Initialize self. +See help(type(self)) for accurate signature.

    +
    +
    +def AddFriend(self, firstname, lastname, email, devices={}, admin=False) +
    +
    +

    This API will send an email to a user and if they accept, will give them access to the devices you specify. +NOTE: XXX-XXXXXXX_XXXXXXXXXXXX is the uniqueId field in your device object.

    +

    {adminUser:false,firstName:John,lastName:Doe,email:john.doe@example.com,devices:{XXX-XXXXXXX_XXXXXXXXXXXX:Camera1,XXX-XXXXXXX_XXXXXXXXXXXX:Camera2}}

    +
    +
    +def AdjustBrightness(self, basestation, camera, brightness=0) +
    +
    +

    NOTE: Brightness is between -2 and 2 in increments of 1 (-2, -1, 0, 1, 2). +Setting it to an invalid value has no effect.

    +

    Returns: +{ +"action": "is", +"from": "XXXXXXXXXXXXX", +"properties": { +"brightness": -2 +}, +"resource": "cameras/XXXXXXXXXXXXX", +"to": "336-XXXXXXX_web", +"transId": "web!XXXXXXXX.389518!1514956240683" +}

    +
    +
    +def AlertNotificationMethods(self, basestation, action='disabled', email=False, push=False) +
    +
    +
    +
    action : disabled OR recordSnapshot OR recordVideo
    +
     
    +
    +
    +
    +def Arm(self, device) +
    +
    +
    +
    +
    +def BatchDeleteRecordings(self, recordings) +
    +
    +

    Delete a batch of video recordings from Arlo.

    +

    The GetLibrary() call response json can be passed directly to this method if you'd like to delete the same list of videos you queried for. +If you want to delete some other batch of videos, then you need to send an array of objects representing each video you want to delete.

    +

    [ +{ +"createdDate":"20160904", +"utcCreatedDate":1473010280395, +"deviceId":"XXXXXXXXXXXXX" +}, +{ +"createdDate":"20160904", +"utcCreatedDate":1473010280395, +"deviceId":"XXXXXXXXXXXXX" +} +]

    +
    +
    +def Calendar(self, basestation, active=True) +
    +
    +

    DEPRECATED: This API appears to still do stuff, but I don't see it called in the web UI anymore when switching the mode to a schedule.

    +

    NOTE: The Arlo API seems to disable calendar mode when switching to other modes, if it's enabled. +You should probably do the same, although, the UI reflects the switch from calendar mode to say armed mode without explicitly setting calendar mode to inactive.

    +
    +
    +def CustomMode(self, device, mode, schedules=[]) +
    +
    +

    device can be any object that has parentId == deviceId. i.e., not a camera

    +
    +
    +def DeleteMode(self, device, mode) +
    +
    +

    device can be any object that has parentId == deviceId. i.e., not a camera

    +
    +
    +def DeleteRecording(self, recording) +
    +
    +

    Delete a single video recording from Arlo. +All of the date info and device id you need to pass into this method are given in the results of the GetLibrary() call.

    +
    +
    +def Disarm(self, device) +
    +
    +
    +
    +
    +def DownloadRecording(self, url, to) +
    +
    +

    Writes a video to a given local file path.

    +
    +
    url : presignedContentUrl
    +
     
    +
    to : path where the file should be written
    +
     
    +
    +
    +
    +def DownloadSnapshot(self, url, to, chunk_size=4096) +
    +
    +

    Writes a snapshot to a given local file path.

    +
    +
    url : presignedContentUrl or presignedFullFrameSnapshotUrl
    +
     
    +
    to : path where the file should be written
    +
     
    +
    +
    +
    +def Geofencing(self, location_id, active=True) +
    +
    +

    Get location_id is the id field from the return of GetLocations() +NOTE: The Arlo API seems to disable geofencing mode when switching to other modes, if it's enabled. +You should probably do the same, although, the UI reflects the switch from calendar mode to say armed mode without explicitly setting calendar mode to inactive.

    +
    +
    +def GetAudioPlayback(self, basestation) +
    +
    +
    +
    +
    +def GetAutomationActivityZones(self, camera) +
    +
    +
    +
    +
    +def GetAutomationDefinitions(self) +
    +
    +
    +
    +
    +def GetBaseStationState(self, basestation) +
    +
    +
    +
    +
    +def GetCalendar(self, basestation) +
    +
    +
    +
    +
    +def GetCameraState(self, basestation) +
    +
    +
    +
    +
    +def GetCameraTempReading(self, basestation) +
    +
    +
    +
    +
    +def GetCvrPlaylist(self, camera, fromDate, toDate) +
    +
    +

    This function downloads a Cvr Playlist file for the period fromDate to toDate.

    +
    +
    +def GetDeviceCapabilities(self, device) +
    +
    +
    +
    +
    +def GetDeviceSupport(self) +
    +
    +

    DEPRECATED: This API still works, but I don't see it being called in the web UI anymore.

    +

    This API looks like it's mainly used by the website, but I'm including it for completeness sake. +It returns something like the following: +{ +"devices": [ +{ +"deviceType": "arloq", +"urls": { +"troubleshoot": "https://vzs3-prod-common.s3.amazonaws.com/static/html/en/pc_troubleshoot.html", +"plugin": "https://vzs3-prod-common.s3.amazonaws.com/static/html/en/pc_plugin.html", +"connection": "https://vzs3-prod-common.s3.amazonaws.com/static/html/en/pc_connection.html", +"connectionFailed": "https://vzs3-prod-common.s3.amazonaws.com/static/html/en/pc_connection_fail.html", +"press_sync": "https://vzs3-prod-common.s3. amazonaws.com/static/html/en/pc_press_sync.html", +"resetDevice": "https://vzs3-prod-common.s3.amazonaws.com/static/html/en/reset_arloq.html", +"qr_how_to": "https://vzs3-prod-common.s3.amazonaws.com/static/html/en/pc_qr_how_to.html" +} +}, +{ +"deviceType": "basestation", +"urls": { +"troubleshoot": "https://vzs3-prod-common.s3.amazonaws.com/static/html/en/bs_troubleshoot.html", +"connection": "https://vzs3-prod-common.s3.amazonaws.com/static/html/en/bs_connection.html", +"sync": "https://vzs3-prod-common.s3.amazonaws.com/static/html/en/bs_sync_camera.html" +} +}, +{ +"deviceType": "arloqs", +"urls": { +"ethernetSetup": "https://vzs3-prod-common.s3.amazonaws.com/static/html/en/arloqs/ethernet_setup.html", +"plugin": "https:// +vzs3-prod-common.s3.amazonaws.com/static/html/en/arloqs/aqp_plugin.html", +"connectionWiFi": "https://vzs3-prod-common.s3.amazonaws.com/static/html/en/arloqs/connection_in_progress_wifi.html", +"poeSetup": "https://vzs3-prod-common.s3. +amazonaws.com/static/html/en/arloqs/poe_setup.html", +"connection": "https://vzs3-prod-common.s3.amazonaws.com/static/html/en/arloqs/connection_in_progress.html", +"connectionFailed": "https://vzs3-prod-common.s3.amazonaws.com/static/html/en/arloqs/connection_fail.html", +"press_sync": "https://vzs3-prod-common.s3.amazonaws.com/static/html/en/arloqs/press_sync.html", +"connectionType": "https://vzs3-prod-common.s3.amazonaws.com/static/html/en/arloqs/connection_type.html", +"resetDevice": "https://vzs3-prod-common.s3.amazonaws.com/static/html/en/arloqs/reset_device.html", +"qr_how_to": "https://vzs3-prod-common.s3.amazonaws.com/static/html/en/arloqs/qr_how_to.html" +} +} +] +}

    +
    +
    +def GetDeviceSupportV3(self) +
    +
    +

    This is the latest version of the device support api. +It returns something like the following: +{ +"data": { +"devices": { +"camera": { +"modelIds": [ +"VMC3010", +"VMC3030", +"VMC4030", +"VMC4030P", +"VMC5040", +"VZC3010", +"VZC3030" +], +"connectionTypes": { +"WPS": true, +"BLE": true +}, +"kbArticles": { +"insertBatteries": "https://kb.arlo.com/980150/Safety-Rules-for-Arlo-Wire-Free-Camera-Batteries", +"syncBasestation": "https://kb.arlo.com/987/How-do-I-set-up-and-sync-my-Arlo-Wire-Free-cameras", +"sync": "https://kb.arlo.com/987/How-do-I-set-up-and-sync-my-Arlo-Wire-Free-camera", +"firmwareUpdate": "https://kb.arlo.com/4736/How-do-I-update-my-Arlo-firmware-manually" +} +}, +"arloq": { +"modelIds": [ +"VMC3040", +"VMC3040S" +], +"kbArticles": { +"power": "https://kb.arlo.com/1001944/How-do-I-set-up-Arlo-Q-on-iOS", +"qrCode": "https://kb.arlo.com/1001944/How-do-I-set-up-Arlo-Q-on-iOS", +"power_android": "https://kb.arlo.com/1002006/How-do-I-set-up-Arlo-Q-on-Android", +"qrCode_android": +"https://kb.arlo.com/1002006/How-do-I-set-up-Arlo-Q-on-Android" +} +}, +"basestation": { +"modelIds": [ +"VMB3010", +"VMB4000", +"VMB3010r2", +"VMB3500", +"VZB3010", +"VMB4500", +"VMB5000" +], +"smartHubs": [ +"VMB5000" +], +"kbArticles": { +"pluginNetworkCable": "https://kb.arlo.com/1179139/How-do-I-connect-my-Arlo-or-Arlo-Pro-base-station-to-the-Internet", +"power": "https://kb.arlo.com/1179139/How-do-I-connect-my-Arlo-or-Arlo-Pro-base-station-to-the-Internet", +"led": "https://kb.arlo.com/1179139/How-do-I-connect-my-Arlo-or-Arlo-Pro-base-station-to-the-Internet", +"learnMore": "https://kb.arlo.com/000062124/How-do-I-record-4K-videos-to-a-microSD-card" +} +}, +"arlobaby": { +"modelIds": [ +"ABC1000" +], +"kbArticles": { +"power": "https://kb.arlo.com/1282682/How-do-I-power-cycle-my-Arlo-Baby-camera", +"qrCode": "https://kb.arlo.com/1282700/How-do-I-set-up-my-Arlo-Baby-camera" +} +}, +"lteCamera":{ +"modelIds":[ +"VML4030" +], +"kbArticles":{ +"servicePlan":"https://kb.arlo.com/1286865/What-Arlo-Mobile-service-plans-are-available", +"simActivation":"https://kb.arlo.com/1286865/What-Arlo-Mobile-service-plans-are-available", +"qrCode":"https://kb.arlo.com/1201822/How-do-I-set-up-my-Arlo-Go-camera" +} +}, +"bridge": { +"modelIds": [ +"ABB1000" +], +"kbArticles": { +"power": "https://kb.arlo.com/000062047", +"sync": "https://kb.arlo.com/000062037", +"qrCode": "https://kb.arlo.com/000061886", +"factoryReset": "https://kb.arlo.com/000061837" +} +}, +"lights": { +"modelIds": [ +"AL1101" +], +"kbArticles": { +"sync": "https://kb.arlo.com/000062005", +"insertBatteries": "https://kb.arlo.com/000061952", +"qrCode": "https://kb.arlo.com/000061886" +} +}, +"routerM1":{ +"modelIds":[ +"MR1100" +], +"kbArticles":{ +"lookupFailed":"https://kb.arlo.com/1179130/Arlo-can-t-discover-my-base-station-during-installation-what-do-I-do" +} +}, +"chime": { +"modelIds": [ +"AC1001" +], +"kbArticles": { +"ledNotBlinking":"https://kb.arlo.com/000061924", +"led":"https://kb.arlo.com/000061847", +"factoryReset":"https://kb.arlo.com/000061879", +"connectionFailed":"https://kb.arlo.com/000061880" +} +}, +"doorbell": { +"modelIds": [ +"AAD1001" +], +"kbArticles": { +"led":"https://kb.arlo.com/000061847", +"factoryReset":"https://kb.arlo.com/000061842", +"pairCamera":"https://kb.arlo.com/000061897", +"existingChime":"https://kb.arlo.com/000061856", +"noWiring":"https://kb.arlo.com/000061859", +"connectionFailed":"https://kb.arlo.com/000061868", +"pairCameraFailed":"https://kb.arlo.com/000061893", +"testChimeFailed":"https://kb.arlo.com/000061944" +}, +"videos": { +"chimeType": "https://youtu.be/axytuF63VC0", +"wireDoorbell": "https://youtu.be/_5D2n3iPqW0", +"switchSetting": "https://youtu.be/BUmd4fik2RE" +}, +"arloVideos": { +"chimeType": "https://vzs3-prod-common.s3.amazonaws.com/static/devicesupport/Arlo_Audio_Doorbell_Chime.mp4", +"wireDoorbell": "https://vzs3-prod-common.s3.amazonaws.com/static/devicesupport/Arlo_Audio_Doorbell_Wired.mp4", +"switchSetting": "https://vzs3-prod-common.s3.amazonaws.com/static/devicesupport/Arlo_Audio_Doorbell_Switch.mp4" +} +} +}, +"arlosmart": { +"kbArticles": { +"e911": "https://www.arlo.com/en-us/landing/arlosmart/", +"callFriend": "https://www.arlo.com/en-us/landing/arlosmart/", +"4kAddOnPopup": "https://www.arlo.com/en-us/landing/arlosmart/", +"cloudRecording": "https://www.arlo.com/en-us/landing/arlosmart/", +"manageArloSmart": "https://kb.arlo.com/000062115", +"otherVideo": "https://kb.arlo.com/000062115", +"packageDetection": "https://kb.arlo.com/000062114", +"whereIsBasicSubscriptionGone": "https://kb.arlo.com/000062163" +} +} +}, +"success":true +}

    +
    +
    +def GetDeviceSupportv2(self) +
    +
    +

    DEPRECATED: This API still works, but I don't see it being called in the web UI anymore.

    +

    It returns something like the following: +{ +"devices": [ +{ +"deviceType": "arloq", +"modelId": [ +"VMC3040" +], +"urls": { +"troubleshoot": "arloq/troubleshoot.html", +"plugin": "arloq/plugin.html", +"qrHowTo": "arloq/qrHowTo.html", +"connection": "arloq/connection.html", +"connectionInProgress": "arloq/connectionInProgress.html", +"connectionFailed": "arloq/connectionFailed.html", +"pressSync": "arloq/pressSync.html", +"resetDevice": "arloq/resetDevice.html" +} +}, +{ +"deviceType": "basestation", +"modelId": [ +"VMB3010", +"VMB3010r2", +"VMB3500", +"VMB4000", +"VMB4500", +"VZB3010" +], +"urls": { +"troubleshoot": "basestation/troubleshoot.html", +"plugin": "basestation/plugin.html", +"sync3": "basestation/sync3.html", +"troubleshootBS": "basestation/troubleshootBS.html", +"connection": "basestation/connection.html", +"connectionInProgress": "basestation/connectionInProgress.html", +"sync2": "basestation/sync2.html", +"connectionFailed": "basestation/connectionFailed.html", +"sync1": "basestation/sync1.html", +"resetDevice": "basestation/resetDevice.html", +"syncComplete": "basestation/syncComplete.html" +} +}, +{ +"deviceType": "arlobaby", +"modelId": [ +"ABC1000" +], +"urls": { +"bleSetupError": "arlobaby/bleSetupError.html", +"troubleshoot": "arlobaby/troubleshoot.html", +"homekitCodeInstruction": "arlobaby/homekitCodeInstruction.html", +"connectionInProgress": "arlobaby/connectionInProgress.html", +"connectionFailed": "arlobaby/connectionFailed.html", +"resetDevice": "arlobaby/resetDevice.html", +"plugin": "arlobaby/plugin.html", +"qrHowTo": "arlobaby/qrHowTo.html", +"warning": "arlobaby/warning.html", +"connection": "arlobaby/connection.html", +"pressSync": "arlobaby/pressSync.html", +"bleInactive": "arlobaby/bleInactive.html", +"pluginIOS": "arlobaby/pluginIOS.html", +"homekitSetup": "arlobaby/homekitSetup.html" +} +}, +{ +"deviceType": "lteCamera", +"modelId": [ +"VML4030" +], +"urls": { +"troubleshoot": "lteCamera/troubleshoot.html", +"resetHowTo": "lteCamera/resetHowTo.html", +"plugin": "lteCamera/plugin.html", +"qrHowTo": "lteCamera/qrHowTo.html", +"connectionInProgress": "lteCamera/connectionInProgress.html", +"connectionFailed": "lteCamera/connectionFailed.html", +"resetDevice": "lteCamera/resetHowTo.html", +"resetComplete": "lteCamera/resetComplete.html", +"syncComplete": "lteCamera/syncComplete.html" +} +}, +{ +"deviceType": "arloqs", +"modelId": [ +"VMC3040S" +], +"urls": { +"ethernetSetup": "arloqs/ethernetSetup.html", +"troubleshoot": "arloqs/troubleshoot.html", +"plugin": "arloqs/plugin.html", +"poeSetup": "arloqs/poeSetup.html", +"connectionInProgressWiFi": "arloqs/connectionInProgressWifi.html", +"qrHowTo": "arloqs/qrHowTo.html", +"connectionInProgress": "arloqs/connectionInProgress.html", +"connectionFailed": "arloqs/connectionFailed.html", +"pressSync": "arloqs/pressSync.html", +"connectionType": "arloqs/connectionType.html", +"resetDevice": "arloqs/resetDevice.html" +} +}, +{ +"deviceType": "bridge", +"modelId": [ +"ABB1000" +], +"urls": { +"troubleshoot": "bridge/troubleshoot.html", +"fwUpdateInProgress": "bridge/fwUpdateInProgress.html", +"qrHowToUnplug": "bridge/qrHowToUnplug.html", +"fwUpdateDone": "bridge/fwUpdateDone.html", +"fwUpdateAvailable": "bridge/fwUpdateAvailable.html", +"needHelp": "https://www.arlo.com/en-us/support/#support_arlo_light", +"wifiError": "bridge/wifiError.html", +"bleAndroid": "bridge/bleInactiveAND.html", +"bleIOS": "bridge/bleInactiveIOS.html", +"connectionInProgress": "bridge/connectionInProgress.html", +"connectionFailed": "bridge/connectionFailed.html", +"manualPair": "bridge/manualPairing.html", +"resetDevice": "bridge/resetDevice.html", +"lowPower": "bridge/lowPowerZoneSetup.html", +"fwUpdateFailed": "bridge/fwUpdateFailed.html", +"fwUpdateCheckFailed": "bridge/fwUpdateCheckFailed.html", +"plugin": "bridge/plugin.html", +"qrHowTo": "bridge/qrHowTo.html", +"pressSync": "bridge/pressSync.html", +"pluginNoLED": "bridge/pluginNoLED.html", +"fwUpdateCheck": "bridge/fwUpdateCheck.html" +} +}, +{ +"deviceType": "lights", +"modelId": [ +"AL1101" +], +"urls": { +"troubleshoot": "lights/troubleshoot.html", +"needHelp": "https://kb.netgear.com/000053159/Light-discovery-failed.html", +"bleInactiveAND": "lights/bleInactiveAND.html", +"connectionInProgress": "lights/connectionInProgress.html", +"connectionFailed": "lights/connectionFailed.html", +"addBattery": "lights/addBattery.html", +"tutorial1": "lights/tutorial1.html", +"plugin": "lights/plugin.html", +"tutorial2": "lights/tutorial2.html", +"tutorial3": "lights/tutorial3.html", +"configurationInProgress": "lights/configurationInProgress.html", +"qrHowTo": "lights/qrHowTo.html", +"pressSync": "lights/pressSync.html", +"bleInactiveIOS": "lights/bleInactiveIOS.html", +"syncComplete": "lights/syncComplete.html" +} +}, +{ +"deviceType": "routerM1", +"modelId": [ +"MR1100" +], +"urls": { +"troubleshoot": "routerM1/troubleshoot.html", +"help": "routerM1/help.html", +"pairingFailed": "routerM1/pairingFailed.html", +"needHelp": "https://acupdates.netgear.com/help/redirect.aspx?url=m1arlo-kbb", +"plugin": "routerM1/plugin.html", +"pairing": "routerM1/pairing.html", +"connectionInProgress": "routerM1/connectionInProgress.html", +"sync2": "routerM1/sync2.html", +"connectionFailed": "routerM1/connectionFailed.html", +"sync1": "routerM1/sync1.html", +"sync": "routerM1/sync.html", +"syncComplete": "routerM1/syncComplete.html" +} +} +], +"selectionUrls": { +"addDevice": "addDeviceBsRuAqAqpLteAbcMrBgLt.html", +"selectBasestation": "selectBsMr.html", +"deviceSelection": "deviceBsAqAqpLteAbcMrLtSelection.html", +"selectLights": "selectBgLt.html" +}, +"baseUrl": "https://vzs3-prod-common.s3.amazonaws.com/static/v2/html/en/" +}

    +
    +
    +def GetDevices(self, device_type=None, filter_provisioned=None) +
    +
    +

    This method returns an array that contains the basestation, cameras, etc. and their metadata. +If you pass in a valid device type, as a string or a list, this method will return an array of just those devices that match that type. An example would be ['basestation', 'camera'] +To filter provisioned or unprovisioned devices pass in a True/False value for filter_provisioned. By default both types are returned.

    +
    +
    +def GetEmergencyLocations(self) +
    +
    +
    +
    +
    +def GetFriends(self) +
    +
    +
    +
    +
    +def GetLibrary(self, from_date, to_date) +
    +
    +

    This call returns the following: +presignedContentUrl is a link to the actual video in Amazon AWS. +presignedThumbnailUrl is a link to the thumbnail .jpg of the actual video in Amazon AWS.

    +

    [ +{ +"mediaDurationSecond": 30, +"contentType": "video/mp4", +"name": "XXXXXXXXXXXXX", +"presignedContentUrl": "https://arlos3-prod-z2.s3.amazonaws.com/XXXXXXX_XXXX_XXXX_XXXX_XXXXXXXXXXXXX/XXX-XXXXXXX/XXXXXXXXXXXXX/recordings/XXXXXXXXXXXXX.mp4?AWSAccessKeyId=XXXXXXXXXXXXXXXXXXXX&Expires=1472968703&Signature=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", +"lastModified": 1472881430181, +"localCreatedDate": XXXXXXXXXXXXX, +"presignedThumbnailUrl": "https://arlos3-prod-z2.s3.amazonaws.com/XXXXXXX_XXXX_XXXX_XXXX_XXXXXXXXXXXXX/XXX-XXXXXXX/XXXXXXXXXXXXX/recordings/XXXXXXXXXXXXX_thumb.jpg?AWSAccessKeyId=XXXXXXXXXXXXXXXXXXXX&Expires=1472968703&Signature=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", +"reason": "motionRecord", +"deviceId": "XXXXXXXXXXXXX", +"createdBy": "XXXXXXXXXXXXX", +"createdDate": "20160903", +"timeZone": "America/Chicago", +"ownerId": "XXX-XXXXXXX", +"utcCreatedDate": XXXXXXXXXXXXX, +"currentState": "new", +"mediaDuration": "00:00:30" +} +]

    +
    +
    +def GetLibraryMetaData(self, from_date, to_date) +
    +
    +
    +
    +
    +def GetLocations(self) +
    +
    +

    This call returns the following: +{ +"id":"XXX-XXXXXXX_20160823042047", +"name":"Home", +"ownerId":"XXX-XXXXXXX", +"longitude":X.XXXXXXXXXXXXXXXX, +"latitude":X.XXXXXXXXXXXXXXXX, +"address":"123 Middle Of Nowhere Bumbfuck, EG, 12345", +"homeMode":"schedule", +"awayMode":"mode1", +"geoEnabled":false, +"geoRadius":150.0, +"uniqueIds":[ +"XXX-XXXXXXX_XXXXXXXXXXXXX" +], +"smartDevices":[ +"XXXXXXXXXX", +"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" +], +"pushNotifyDevices":[ +"XXXXXXXXXX" +] +}

    +
    +
    +def GetModes(self, basestation) +
    +
    +

    DEPRECATED: This is the older API for getting the "mode". It still works, but GetModesV2 is the way the Arlo software does it these days.

    +
    +
    +def GetModesV2(self) +
    +
    +

    This is the newer API for getting the "mode". This method also returns the schedules. +Set a non-schedule mode to be active: {"activeAutomations":[{"deviceId":"XXXXXXXXXXXXX","timestamp":1532015622105,"activeModes":["mode1"],"activeSchedules":[]}]} +Set a schedule to be active: {"activeAutomations":[{"deviceId":"XXXXXXXXXXXXX","timestamp":1532015790139,"activeModes":[],"activeSchedules":["schedule.1"]}]}

    +
    +
    +def GetOCProfile(self) +
    +
    +
    +
    +
    +def GetPaymentBilling(self) +
    +
    +
    +
    +
    +def GetPaymentOffers(self) +
    +
    +

    DEPRECATED: This API still works, but I don't see it being called in the web UI anymore.

    +
    +
    +def GetPaymentOffersV2(self) +
    +
    +

    DEPRECATED: This API still works, but I don't see it being called in the web UI anymore.

    +
    +
    +def GetPaymentOffersV3(self) +
    +
    +

    DEPRECATED: This API still works, but I don't see it being called in the web UI anymore.

    +
    +
    +def GetPaymentOffersV4(self) +
    +
    +
    +
    +
    +def GetProfile(self) +
    +
    +
    +
    +
    +def GetRecording(self, url, chunk_size=4096) +
    +
    +

    Returns the whole video from the presignedContentUrl.

    +
    +
    +def GetRules(self, basestation) +
    +
    +
    +
    +
    +def GetSensorConfig(self, basestation) +
    +
    +
    +
    +
    +def GetServiceLevel(self) +
    +
    +
    +
    +
    +def GetServiceLevelSettings(self) +
    +
    +
    +
    +
    +def GetServiceLevelV2(self) +
    +
    +

    DEPRECATED: This API still works, but I don't see it being called in the web UI anymore.

    +
    +
    +def GetServiceLevelV3(self) +
    +
    +

    DEPRECATED: This API still works, but I don't see it being called in the web UI anymore.

    +
    +
    +def GetServiceLevelV4(self) +
    +
    +
    +
    +
    +def GetSession(self) +
    +
    +

    Returns something like the following: +{ +"userId": "XXX-XXXXXXX", +"email": "jeffreydwalter@gmail.com", +"token": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", +"paymentId": "XXXXXXXX", +"accountStatus": "registered", +"serialNumber": "XXXXXXXXXXXXXX", +"countryCode": "US", +"tocUpdate": false, +"policyUpdate": false, +"validEmail": true, +"arlo": true, +"dateCreated": 1463975008658 +}

    +
    +
    +def GetSmartAlerts(self, camera) +
    +
    +
    +
    +
    +def GetSmartFeatures(self) +
    +
    +
    +
    +
    +def GetUpdateFeatures(self) +
    +
    +
    +
    +
    +def HandleEvents(self, basestation, callback, timeout=120) +
    +
    +

    Use this method to subscribe to the event stream and provide a callback that will be called for event event received. +This function will allow you to potentially write a callback that can handle all of the events received from the event stream.

    +
    +
    +def Login(self, username, password) +
    +
    +

    This call returns the following: +{ +"userId":"XXX-XXXXXXX", +"email":"user@example.com", +"token":"2_5HicFJMXXXXX-S_7IuK2EqOUHXXXXXXXXXXX1CXKWTThgU18Va_XXXXXX5S00hUafv3PV_if_Bl_rhiFsDHYwhxI3CxlVnR5f3q2XXXXXX-Wnt9F7D82uN1f4cXXXXX-FMUsWF_6tMBqwn6DpzOaIB7ciJrnr2QJyKewbQouGM6", +"paymentId":"XXXXXXXX", +"authenticated":1472961381, +"accountStatus":"registered", +"serialNumber":"XXXXXXXXXXXXX", +"countryCode":"US", +"tocUpdate":false, +"policyUpdate":false, +"validEmail":true +}

    +
    +
    +def Logout(self) +
    +
    +
    +
    +
    +def Notify(self, basestation, body) +
    +
    +

    The following are examples of the json you would need to pass in the body of the Notify() call to interact with Arlo:

    +
    +
    +
    +
    NOTE : While you can call Notify() directly, responses from these notify calls are sent to the EventStream (see Subscribe()),
    +
     
    +
    +

    and so it's better to use the Get/Set methods that are implemented using the NotifyAndGetResponse() method.

    +
    +
    +

    Set System Mode (Armed, Disarmed) - {"from":"XXX-XXXXXXX_web","to":"XXXXXXXXXXXXX","action":"set","resource":"modes","transId":"web!XXXXXXXX.XXXXXXXXXXXXXXXXXXXX","publishResponse":true,"properties":{"active":"mode0"}} +Set System Mode (Calendar) - {"from":"XXX-XXXXXXX_web","to":"XXXXXXXXXXXXX","action":"set","resource":"schedule","transId":"web!XXXXXXXX.XXXXXXXXXXXXXXXXXXXX","publishResponse":true,"properties":{"active":true}} +Configure The Schedule (Calendar) - {"from":"XXX-XXXXXXX_web","to":"XXXXXXXXXXXXX","action":"set","resource":"schedule","transId":"web!XXXXXXXX.XXXXXXXXXXXXXXXXXXXX","publishResponse":true,"properties":{"schedule":[{"modeId":"mode0","startTime":0},{"modeId":"mode2","startTime":28800000},{"modeId":"mode0","startTime":64800000},{"modeId":"mode0","startTime":86400000},{"modeId":"mode2","startTime":115200000},{"modeId":"mode0","startTime":151200000},{"modeId":"mode0","startTime":172800000},{"modeId":"mode2","startTime":201600000},{"modeId":"mode0","startTime":237600000},{"modeId":"mode0","startTime":259200000},{"modeId":"mode2","startTime":288000000},{"modeId":"mode0","startTime":324000000},{"modeId":"mode0","startTime":345600000},{"modeId":"mode2","startTime":374400000},{"modeId":"mode0","startTime":410400000},{"modeId":"mode0","startTime":432000000},{"modeId":"mode0","startTime":518400000}]} +Create Mode - +{"from":"XXX-XXXXXXX_web","to":"XXXXXXXXXXXXX","action":"add","resource":"rules","transId":"web!XXXXXXXX.XXXXXXXXXXXXXXXXXXXX","publishResponse":true,"properties":{"name":"Record video on Camera 1 if Camera 1 detects motion","id":"ruleNew","triggers":[{"type":"pirMotionActive","deviceId":"XXXXXXXXXXXXX","sensitivity":80}],"actions":[{"deviceId":"XXXXXXXXXXXXX","type":"recordVideo","stopCondition":{"type":"timeout","timeout":15}},{"type":"sendEmailAlert","recipients":["OWNER_EMAIL"]},{"type":"pushNotification"}]}} +{"from":"XXX-XXXXXXX_web","to":"XXXXXXXXXXXXX","action":"add","resource":"modes","transId":"web!XXXXXXXX.XXXXXXXXXXXXXXXXXXXX","publishResponse":true,"properties":{"name":"Test","rules":["rule3"]}} +Delete Mode - {"from":"XXX-XXXXXXX_web","to":"XXXXXXXXXXXXX","action":"delete","resource":"modes/mode3","transId":"web!XXXXXXXX.XXXXXXXXXXXXXXXXXXXX","publishResponse":true} +Camera Off - {"from":"XXX-XXXXXXX_web","to":"XXXXXXXXXXXXX","action":"set","resource":"cameras/XXXXXXXXXXXXX","transId":"web!XXXXXXXX.XXXXXXXXXXXXXXXXXXXX","publishResponse":true,"properties":{"privacyActive":false}} +Night Vision On - {"from":"XXX-XXXXXXX_web","to":"XXXXXXXXXXXXX","action":"set","resource":"cameras/XXXXXXXXXXXXX","transId":"web!XXXXXXXX.XXXXXXXXXXXXXXXXXXXX","publishResponse":true,"properties":{"zoom":{"topleftx":0,"toplefty":0,"bottomrightx":1280,"bottomrighty":720},"mirror":true,"flip":true,"nightVisionMode":1,"powerSaveMode":2}} +Motion Detection Test - {"from":"XXX-XXXXXXX_web","to":"XXXXXXXXXXXXX","action":"set","resource":"cameras/XXXXXXXXXXXXX","transId":"web!XXXXXXXX.XXXXXXXXXXXXXXXXXXXX","publishResponse":true,"properties":{"motionSetupModeEnabled":true,"motionSetupModeSensitivity":80}}

    +

    device_id = locations.data.uniqueIds

    +

    System Properties: ("resource":"modes") +active (string) - Mode Selection (mode2 = All Motion On, mode1 = Armed, mode0 = Disarmed, etc.)

    +

    System Properties: ("resource":"schedule") +active (bool) - Mode Selection (true = Calendar)

    +

    Camera Properties: ("resource":"cameras/{id}") +privacyActive (bool) - Camera On/Off +zoom (topleftx (int), toplefty (int), bottomrightx (int), bottomrighty (int)) - Camera Zoom Level +mirror (bool) - Mirror Image (left-to-right or right-to-left) +flip (bool) - Flip Image Vertically +nightVisionMode (int) - Night Mode Enabled/Disabled (1, 0) +powerSaveMode (int) - PowerSaver Mode (3 = Best Video, 2 = Optimized, 1 = Best Battery Life) +motionSetupModeEnabled (bool) - Motion Detection Setup Enabled/Disabled +motionSetupModeSensitivity (int 0-100) - Motion Detection Sensitivity

    +
    +
    +def NotifyAndGetResponse(self, basestation, body, timeout=120) +
    +
    +
    +
    +
    +def PauseTrack(self, basestation) +
    +
    +
    +
    +
    +def Ping(self, basestation) +
    +
    +
    +
    +
    +def PlayTrack(self, basestation, track_id='2391d620-e491-4412-99f6-e9a40d6046ed', position=0) +
    +
    +

    Defaulting to 'hugh little baby', which is a supplied track. I hope the ID is the same for all.

    +
    +
    +def PushToTalk(self, camera) +
    +
    +
    +
    +
    +def RemoveFriend(self, email) +
    +
    +

    Removes a person you've granted access to.

    +

    email: email of user you want to revoke access from.

    +
    +
    +def ResendFriendInvite(self, friend) +
    +
    +
    +
    This API will resend an invitation email to a user that you've AddFriend'd. You will need to get the friend object by calling GetFriend() because it includes a token that must be passed to this API.
    +
    friend : {"ownerId":"XXX-XXXXXXX","token":"really long string that you get from the GetFriends() API","firstName":"John","lastName":"Doe","devices":{"XXX-XXXXXXX_XXXXXXXXXXXX":"Camera1","XXX-XXXXXXX_XXXXXXXXXXXX":"Camera2"},"lastModified":1548470485419,"adminUser":false,"email":"john.doe@example.com"}
    +
     
    +
    +
    +
    +def Reset(self) +
    +
    +
    +
    +
    +def RestartBasestation(self, basestation) +
    +
    +
    +
    +
    +def SetAirQualityAlertOff(self, basestation) +
    +
    +
    +
    +
    +def SetAirQualityAlertOn(self, basestation) +
    +
    +
    +
    +
    +def SetAirQualityAlertThresholdMax(self, basestation, number=700) +
    +
    +
    +
    +
    +def SetAirQualityAlertThresholdMin(self, basestation, number=400) +
    +
    +
    +
    +
    +def SetAirQualityRecordingOff(self, basestation) +
    +
    +
    +
    +
    +def SetAirQualityRecordingOn(self, basestation) +
    +
    +
    +
    +
    +def SetAudioAlertsOff(self, basestation, sensitivity=3) +
    +
    +
    +
    +
    +def SetAudioAlertsOn(self, basestation, sensitivity=3) +
    +
    +
    +
    +
    +def SetAutomationActivityZones(self, camera, zone, coords, color) +
    +
    +

    An activity zone is the area you draw in your video in the UI to tell Arlo what part of the scene to "watch". +This method takes 4 arguments. +camera: the camera you want to set an activity zone for. +name: "Zone 1" - the name of your activity zone. +coords: [{"x":0.37946943483275664,"y":0.3790983606557377},{"x":0.8685121107266436,"y":0.3790983606557377},{"x":0.8685121107266436,"y":1},{"x":0.37946943483275664,"y":1}] - these coordinates are the bonding box for the activity zone. +color: 45136 - the color for your bounding box.

    +
    +
    +def SetHumidityAlertOff(self, basestation) +
    +
    +
    +
    +
    +def SetHumidityAlertOn(self, basestation) +
    +
    +
    +
    +
    +def SetHumidityAlertThresholdMax(self, basestation, number=800) +
    +
    +
    +
    +
    +def SetHumidityAlertThresholdMin(self, basestation, number=400) +
    +
    +
    +
    +
    +def SetHumidityRecordingOff(self, basestation) +
    +
    +
    +
    +
    +def SetHumidityRecordingOn(self, basestation) +
    +
    +
    +
    +
    +def SetLoopBackModeContinuous(self, basestation) +
    +
    +
    +
    +
    +def SetLoopBackModeSingleTrack(self, basestation) +
    +
    +
    +
    +
    +def SetMotionAlertsOff(self, basestation, sensitivity=5) +
    +
    +
    +
    +
    +def SetMotionAlertsOn(self, basestation, sensitivity=5) +
    +
    +
    +
    +
    +def SetNightLightBrightness(self, basestation, level=200) +
    +
    +
    +
    +
    +def SetNightLightColor(self, basestation, red=255, green=255, blue=255) +
    +
    +
    +
    +
    +def SetNightLightMode(self, basestation, mode='rainbow') +
    +
    +

    mode: rainbow or rgb.

    +
    +
    +def SetNightLightOff(self, basestation) +
    +
    +
    +
    +
    +def SetNightLightOn(self, basestation) +
    +
    +
    +
    +
    +def SetNightLightTimerOff(self, basestation, time=0, timediff=300) +
    +
    +
    +
    +
    +def SetNightLightTimerOn(self, basestation, time=1576435472, timediff=0) +
    +
    +
    +
    +
    +def SetOCProfile(self, firstName, lastName, country='United States', language='en', spam_me=0) +
    +
    +
    +
    +
    +def SetSchedule(self, basestation, schedule) +
    +
    +

    The following json is what was sent to the API when I edited my schedule. It contains all of the data necessary to configure a whole week. It's a little convoluted, but you can just play around with the scheduler in Chrome and watch the schema that gets sent.

    +

    { +"schedule": [ +{ +"duration": 600, +"startActions": { +"disableModes": [ +"mode0" +], +"enableModes": [ +"mode1" +] +}, +"days": [ +"Mo", +"Tu", +"We", +"Th", +"Fr", +"Sa", +"Su" +], +"startTime": 0, +"type": "weeklyAction", +"endActions": { +"disableModes": [ +"mode1" +], +"enableModes": [ +"mode0" +] +} +}, +{ +"duration": 360, +"startActions": { +"disableModes": [ +"mode0" +], +"enableModes": [ +"mode2" +] +}, +"days": [ +"Mo", +"Tu", +"We", +"Th", +"Fr", +"Sa", +"Su" +], +"startTime": 1080, +"type": "weeklyAction", +"endActions": { +"disableModes": [ +"mode2" +], +"enableModes": [ +"mode0" +] +} +}, +{ +"duration": 480, +"startActions": { +"disableModes": [ +"mode0" +], +"enableModes": [ +"mode3" +] +}, +"days": [ +"Tu" +], +"startTime": 600, +"type": "weeklyAction", +"endActions": { +"disableModes": [ +"mode3" +], +"enableModes": [ +"mode0" +] +} +} +], +"name": "", +"id": "schedule.1", +"enabled": true +}

    +
    +
    +def SetShuffleOff(self, basestation) +
    +
    +
    +
    +
    +def SetShuffleOn(self, basestation) +
    +
    +
    +
    +
    +def SetSleepTimerOff(self, basestation, time=0, timediff=300) +
    +
    +
    +
    +
    +def SetSleepTimerOn(self, basestation, time=1576435472, timediff=0) +
    +
    +
    +
    +
    +def SetTempAlertOff(self, basestation) +
    +
    +
    +
    +
    +def SetTempAlertOn(self, basestation) +
    +
    +
    +
    +
    +def SetTempAlertThresholdMax(self, basestation, number=240) +
    +
    +
    +
    +
    +def SetTempAlertThresholdMin(self, basestation, number=200) +
    +
    +
    +
    +
    +def SetTempRecordingOff(self, basestation) +
    +
    +
    +
    +
    +def SetTempRecordingOn(self, basestation) +
    +
    +
    +
    +
    +def SetTempUnit(self, uniqueId, unit='C') +
    +
    +
    +
    +
    +def SetVolume(self, basestation, mute=False, volume=50) +
    +
    +
    +
    +
    +def SirenOff(self, basestation) +
    +
    +
    +
    +
    +def SirenOn(self, basestation) +
    +
    +
    +
    +
    +def SkipTrack(self, basestation) +
    +
    +
    +
    +
    +def StartRecording(self, basestation, camera) +
    +
    +

    This function causes the camera to start recording. +You can get the timezone from GetDevices().

    +
    +
    +def StartStream(self, basestation, camera) +
    +
    +

    This function returns the url of the rtsp video stream. +This stream needs to be called within 30 seconds or else it becomes invalid. +It can be streamed with: ffmpeg -re -i 'rtsps://' -acodec copy -vcodec copy test.mp4 +The request to /users/devices/startStream returns: { url:rtsp://:443/vzmodulelive?egressToken=b&userAgent=iOS&cameraId=}

    +
    +
    +def StopRecording(self, camera) +
    +
    +

    This function causes the camera to stop recording. +You can get the timezone from GetDevices().

    +
    +
    +def StopStream(self, basestation, camera) +
    +
    +
    +
    +
    +def StreamRecording(self, url, chunk_size=4096) +
    +
    +

    Returns a generator that is the chunked video stream from the presignedContentUrl.

    +
    +
    url : presignedContentUrl
    +
     
    +
    +
    +
    +def Subscribe(self, basestation) +
    +
    +

    Arlo uses the EventStream interface in the browser to do pub/sub style messaging. +Unfortunately, this appears to be the only way Arlo communicates these messages.

    +

    This function makes the initial GET request to /subscribe, which returns the EventStream socket. +Once we have that socket, the API requires a POST request to /notify with the "subscriptionsresource. +This call "registersthe device (which should be the basestation) so that events will be sent to the EventStream +when subsequent calls to /notify are made.

    +

    Since this interface is asynchronous, and this is a quick and dirty hack to get this working, I'm using a thread +to listen to the EventStream. This thread puts events into a queue. Some polling is required (see NotifyAndGetResponse()) because +the event messages aren't guaranteed to be delivered in any specific order, but I wanted to maintain a synchronous style API.

    +

    You generally shouldn't need to call Subscribe() directly, although I'm leaving it "publicfor now.

    +
    +
    +def SubscribeToMotionEvents(self, basestation, callback, timeout=120) +
    +
    +

    Use this method to subscribe to motion events. You must provide a callback function which will get called once per motion event.

    +

    The callback function should have the following signature: +def callback(self, event)

    +

    This is an example of handling a specific event, in reality, you'd probably want to write a callback for HandleEvents() +that has a big switch statement in it to handle all the various events Arlo produces.

    +
    +
    +def ToggleCamera(self, basestation, camera, active=True) +
    +
    +

    active: True - Camera is off. +active: False - Camera is on.

    +
    +
    +def TriggerAndHandleEvent(self, basestation, trigger, callback, timeout=120) +
    +
    +

    Use this method to subscribe to the event stream and provide a callback that will be called for event event received. +This function will allow you to potentially write a callback that can handle all of the events received from the event stream. +NOTE: Use this function if you need to run some code after subscribing to the eventstream, but before your callback to handle the events runs.

    +
    +
    +def TriggerFullFrameSnapshot(self, basestation, camera) +
    +
    +

    This function causes the camera to record a fullframe snapshot. +The presignedFullFrameSnapshotUrl url is returned. +Use DownloadSnapshot() to download the actual image file.

    +
    +
    +def TriggerStreamSnapshot(self, basestation, camera) +
    +
    +

    This function causes the camera to snapshot while recording. +NOTE: You MUST call StartStream() before calling this function. +If you call StartStream(), you have to start reading data from the stream, or streaming will be cancelled +and taking a snapshot may fail (since it requires the stream to be active).

    +

    NOTE: You should not use this function is you just want a snapshot and aren't intending to stream. +Use TriggerFullFrameSnapshot() instead.

    +

    NOTE: Use DownloadSnapshot() to download the actual image file.

    +
    +
    +def UnPauseTrack(self, basestation) +
    +
    +
    +
    +
    +def Unsubscribe(self, basestation) +
    +
    +

    This method stops the EventStream subscription and removes it from the event_stream collection.

    +
    +
    +def UpdateDeviceName(self, device, name) +
    +
    +
    +
    +
    +def UpdateDisplayOrder(self, body) +
    +
    +

    This is an example of the json you would pass in the body to UpdateDisplayOrder() of your devices in the UI.

    +

    XXXXXXXXXXXXX is the device id of each camera. You can get this from GetDevices(). +{ +"devices":{ +"XXXXXXXXXXXXX":1, +"XXXXXXXXXXXXX":2, +"XXXXXXXXXXXXX":3 +} +}

    +
    +
    +def UpdateFriend(self, body) +
    +
    +

    This is an example of the json you would pass in the body: +{ +"firstName":"Some", +"lastName":"Body", +"devices":{ +"XXXXXXXXXXXXX":"Camera 1", +"XXXXXXXXXXXXX":"Camera 2 ", +"XXXXXXXXXXXXX":"Camera 3" +}, +"lastModified":1463977440911, +"adminUser":true, +"email":"user@example.com", +"id":"XXX-XXXXXXX" +}

    +
    +
    +def UpdatePassword(self, password) +
    +
    +
    +
    +
    +def UpdateProfile(self, first_name, last_name) +
    +
    +
    +
    +
    +def genTransId(self, trans_type='web') +
    +
    +
    +
    +
    +def interrupt_handler(self, signum, frame) +
    +
    +
    +
    +
    +def to_timestamp(self, dt) +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + + diff --git a/RPI Code/Arlo/eventstream.py b/RPI Code/Arlo/eventstream.py new file mode 100644 index 0000000..b23f990 --- /dev/null +++ b/RPI Code/Arlo/eventstream.py @@ -0,0 +1,111 @@ +## +# Copyright 2016 Jeffrey D. Walter +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +## + +import monotonic +import sseclient +import threading +import sys + +if sys.version[0] == '2': + import Queue as queue +else: + import queue as queue + +# TODO: There's a lot more refactoring that could/should be done to abstract out the arlo-specific implementation details. + +class EventStream(object): + """This class provides a queue-based EventStream object.""" + def __init__(self, event_handler, heartbeat_handler, args): + self.connected = False + self.registered = False + self.queue = queue.Queue() + self.heartbeat_stop_event = threading.Event() + self.event_stream_stop_event = threading.Event() + self.arlo = args[0] + self.heartbeat_handler = heartbeat_handler + + try: + event_stream = sseclient.SSEClient('https://my.arlo.com/hmsweb/client/subscribe?token='+self.arlo.request.session.headers.get('Authorization'), session=self.arlo.request.session) + self.event_stream_thread = threading.Thread(name="EventStream", target=event_handler, args=(self.arlo, event_stream, self.event_stream_stop_event, )) + self.event_stream_thread.setDaemon(True) + print(self.arlo.request) + print(self.arlo) + except Exception as e: + raise Exception('Failed to subscribe to eventstream: {0}'.format(e)) + + def __del__(self): + self.Disconnect() + + def Get(self, block=True, timeout=None): + if sys.version[0] == '2' and block: + if timeout: + timeout += monotonic.monotonic() + # If timeout is None, then just pick some arbitrarily large # for the timeout value. + else: + timeout = 1000000 + monotonic.monotonic() + + while True: + try: + # Allow check for Ctrl-C every second + item = self.queue.get(timeout=min(1, timeout - monotonic.monotonic())) + self.queue.task_done() + return item + except queue.Empty: + if monotonic.monotonic() > timeout: + return None + else: + pass + else: + try: + item = self.queue.get(block=block, timeout=timeout) + self.queue.task_done() + return item + except queue.Empty as e: + return None + except Exception as e: + return None + + def Start(self): + self.event_stream_thread.start() + return self + + def Connect(self): + self.connected = True + + def Disconnect(self): + self.connected = False + self.Unregister() + + def Register(self): + self.heartbeat_thread = threading.Thread(name='HeartbeatThread', target=self.heartbeat_handler, args=(self.arlo, self.heartbeat_stop_event, )) + self.heartbeat_thread.setDaemon(True) + self.heartbeat_thread.start() + self.registered = True + + def Unregister(self): + self.registered = False + + if self.queue: + self.queue.put(None) + + self.event_stream_stop_event.set() + self.heartbeat_stop_event.set() + + if self.event_stream_thread != threading.current_thread(): + self.event_stream_thread.join() + + if self.heartbeat_thread != threading.current_thread(): + self.heartbeat_thread.join() diff --git a/RPI Code/Arlo/examples/arlo-adjustbrightness.py b/RPI Code/Arlo/examples/arlo-adjustbrightness.py new file mode 100644 index 0000000..837a170 --- /dev/null +++ b/RPI Code/Arlo/examples/arlo-adjustbrightness.py @@ -0,0 +1,36 @@ +from arlo import Arlo + +USERNAME = 'user@example.com' +PASSWORD = 'supersecretpassword' + +try: + # Instantiating the Arlo object automatically calls Login(), which returns an oAuth token that gets cached. + # Subsequent successful calls to login will update the oAuth token. + arlo = Arlo(USERNAME, PASSWORD) + # At this point you're logged into Arlo. + + # Get the list of devices and filter on device type to only get the basestation. + # This will return an array which includes all of the basestation's associated metadata. + basestations = arlo.GetDevices('basestation') + + # Get the list of devices and filter on device type to only get the camera. + # This will return an array which includes all of the camera's associated metadata. + cameras = arlo.GetDevices('camera') + + # Set camera brightness to 0%. + #arlo.AdjustBrightness(basestations[0], cameras[0] -2) + + # Set camera brightness to 25%. + #arlo.AdjustBrightness(basestations[0], cameras[0], -1) + + # Set camera brightness to 50%. + arlo.AdjustBrightness(basestations[0], cameras[0], 0) + + # Set camera brightness to 75%. + #arlo.AdjustBrightness(basestations[0], cameras[0], 1) + + # Set camera brightness to 100%. + #arlo.AdjustBrightness(basestations[0], cameras[0], 2) + +except Exception as e: + print(e) diff --git a/RPI Code/Arlo/examples/arlo-cvrdownload.py b/RPI Code/Arlo/examples/arlo-cvrdownload.py new file mode 100644 index 0000000..148c607 --- /dev/null +++ b/RPI Code/Arlo/examples/arlo-cvrdownload.py @@ -0,0 +1,88 @@ +import sys, os +sys.path.append('..') +import requests +from arlo import Arlo +from datetime import timedelta, date +import datetime +import json +import re + +USERNAME = 'user@example.com' +PASSWORD = 'supersecretpassword' + +videopath = 'videos' +cameraNumber = 2 +datetimeFrom = datetime.datetime.strptime('2018-08-11 03:00:00', '%Y-%m-%d %H:%M:%S'); +datetimeTo = datetime.datetime.strptime('2018-08-11 04:00:00', '%Y-%m-%d %H:%M:%S'); + + +try: + print("Downloading cvr videos from " + datetimeFrom.strftime("%Y-%m-%d %H:%M:%S") + " to " + datetimeTo.strftime("%Y-%m-%d %H:%M:%S")) + # Instantiating the Arlo object automatically calls Login(), which returns an oAuth token that gets cached. + # Subsequent successful calls to login will update the oAuth token. + arlo = Arlo(USERNAME, PASSWORD) + # At this point you're logged into Arlo. + + # Get the list of devices and filter on device type to only get the basestation. + # This will return an array which includes all of the basestation's associated metadata. + basestations = arlo.GetDevices('basestation') + + # Get the list of devices and filter on device type to only get the camera. + # This will return an array which includes all of the camera's associated metadata. + cameras = arlo.GetDevices('camera') + + # Get all of the recordings for a date range. + playlist = arlo.GetCvrPlaylist(cameras[cameraNumber], datetimeFrom.strftime("%Y%m%d"), datetimeTo.strftime("%Y%m%d")) + + # If no recordings are available exit + if not playlist['playlist']: + print("No playlist found for camera for the period " + datetimeFrom.strftime("%Y-%m-%d %H:%M:%S") + " and " + datetimeTo.strftime("%Y-%m-%d %H:%M:%S")) + arlo.Logout() + print('Logged out') + sys.exit() + + + # Check if videos folder already exists + if not os.path.exists(videopath): + os.makedirs(videopath) + + # debug to show the playlist json + # print(json.dumps(playlist, indent = 4)) + + # Iterate through each day in the cvr playlist. + for playlistPerDay in playlist['playlist']: + # Iterate through each m3u8 (playlist) file + for recordings in playlist['playlist'][playlistPerDay]: + m3u8 = requests.get(recordings['url']).text.split("\n") + # Iterate the m3u8 file and get all the streams + for m3u8Line in m3u8: + # debug to show the m3u8 file + # print m3u8Line + + # Split the url into parts used for filename (camera id and timestamp) + m = re.match("^http.+([A-Z0-9]{13})_[0-9]{13}_([0-9]{13})", m3u8Line) + if m: + cameraId = m.group(1) + videoTime = datetime.datetime.fromtimestamp(int(m.group(2)) // 1000) + + # If we are within desired range, then download + if videoTime > datetimeFrom and videoTime < datetimeTo: + # Get video as a chunked stream; this function returns a generator. + stream = arlo.StreamRecording(m3u8Line) + videofilename = cameraId + '-' + videoTime.strftime('%Y%m%d-%H%M%S') + '.mp4' + + # Skip files already downloaded + if os.path.isfile(videopath + '/' + videofilename): + print("Video " + videofilename + " already exists") + else: + print('Downloading video ' + videofilename) + with open(videopath + '/' + videofilename, 'wb') as f: + for chunk in stream: + f.write(chunk) + f.close() + + arlo.Logout() + print('Logged out') + +except Exception as e: + print(e) diff --git a/RPI Code/Arlo/examples/arlo-download-bycamera.py b/RPI Code/Arlo/examples/arlo-download-bycamera.py new file mode 100644 index 0000000..330b5d5 --- /dev/null +++ b/RPI Code/Arlo/examples/arlo-download-bycamera.py @@ -0,0 +1,75 @@ +from arlo import Arlo + +from datetime import timedelta, date +import datetime +import sys, os +sys.path.append('..') +#import json + +USERNAME = 'user@example.com' +PASSWORD = 'supersecretpassword' + +videopath = 'videos' + +try: + # Instantiating the Arlo object automatically calls Login(), which returns an oAuth token that gets cached. + # Subsequent successful calls to login will update the oAuth token. + arlo = Arlo(USERNAME, PASSWORD) + # At this point you're logged into Arlo. + + # Get the list of devices and filter on device type to only get the cameras. + # This will return an array which includes all of the canera's associated metadata + # and includes Arlo Q devices, re: https://github.com/jeffreydwalter/arlo/wiki/FAQ#frequently-asked-questions + cameras = arlo.GetDevices('camera') + arloq = arlo.GetDevices('arloq') + arloqs = arlo.GetDevices('arloqs') + + cameras = cameras + arloq + arloqs + cameras_by_id = {} + + # setup a hash where each camera deviceId is assocaited with its name for lookup later + for camera in cameras: + cameras_by_id[camera['deviceId']] = camera['deviceName'] + + today = (date.today() - timedelta(days=0)).strftime("%Y%m%d") + seven_days_ago = (date.today() - timedelta(days=7)).strftime("%Y%m%d") + + # Get all of the recordings for a date range. + library = arlo.GetLibrary(seven_days_ago, today) + + # Check if videos folder already exists + if not os.path.exists(videopath): + os.makedirs(videopath) + + # Iterate through the recordings in the library. + for recording in library: + + # Set the extension based on the content type of the returned media + content_type = recording['contentType'] + extension = '.jpg' if content_type == 'image/jpg' else '.mp4' + + # Grab the camera name to use for the filename from the cameras_by_id hash above + camera_name = cameras_by_id[recording['deviceId']] + + videofilename = camera_name + ' - ' + datetime.datetime.fromtimestamp(int(recording['name']) // 1000).strftime('%Y-%m-%d %H-%M-%S') + extension + + # Download the video and write it to the given path. + arlo.DownloadRecording(recording['presignedContentUrl'], videopath + '/' + videofilename) + + print('Downloaded video ' + videofilename + ' from ' + recording['createdDate'] + '.') + + # Use the following line to print all the data we got for the recording. + #print(json.dumps(recording, indent = 4)) + + # Delete all of the videos you just downloaded from the Arlo library. + # Notice that you can pass the "library" object we got back from the GetLibrary() call. + #result = arlo.BatchDeleteRecordings(library) + + # If we made it here without an exception, then the videos were successfully deleted. + #print ('Batch deletion of videos completed successfully.') + + arlo.Logout() + print('Logged out') + +except Exception as e: + print(e) diff --git a/RPI Code/Arlo/examples/arlo-download.py b/RPI Code/Arlo/examples/arlo-download.py new file mode 100644 index 0000000..b01a13b --- /dev/null +++ b/RPI Code/Arlo/examples/arlo-download.py @@ -0,0 +1,59 @@ +from arlo import Arlo + +from datetime import timedelta, date +import datetime +import sys, os +sys.path.append('..') +#import json + +USERNAME = 'user@example.com' +PASSWORD = 'supersecretpassword' + +videopath = 'videos' + +try: + # Instantiating the Arlo object automatically calls Login(), which returns an oAuth token that gets cached. + # Subsequent successful calls to login will update the oAuth token. + arlo = Arlo(USERNAME, PASSWORD) + # At this point you're logged into Arlo. + + today = (date.today() - timedelta(days=0)).strftime("%Y%m%d") + seven_days_ago = (date.today() - timedelta(days=7)).strftime("%Y%m%d") + + # Get all of the recordings for a date range. + library = arlo.GetLibrary(seven_days_ago, today) + + # Check if videos folder already exists + if not os.path.exists(videopath): + os.makedirs(videopath) + + # Iterate through the recordings in the library. + for recording in library: + # Get video as a chunked stream; this function returns a generator. + stream = arlo.StreamRecording(recording['presignedContentUrl']) + videofilename = datetime.datetime.fromtimestamp( + int(recording['name']) // 1000).strftime( + '%Y-%m-%d %H-%M-%S') + ' ' + recording['uniqueId'] + '.mp4' + with open(videopath + '/' + videofilename, 'wb') as f: + for chunk in stream: + f.write(chunk) + f.close() + + print('Downloaded video ' + videofilename + ' from ' + + recording['createdDate'] + '.') + + # Use the following line to print all the data we got for the recording. + #print(json.dumps(recording, indent = 4)) + + # Delete all of the videos you just downloaded from the Arlo library. + # Notice that you can pass the "library" object we got back from the GetLibrary() call. + #result = arlo.BatchDeleteRecordings(library) + + # If we made it here without an exception, then the videos were successfully deleted. + #print ('Batch deletion of videos completed successfully.') + + arlo.Logout() + print('Logged out') + +except Exception as e: + print(e) diff --git a/RPI Code/Arlo/examples/arlo-fullscreensnapshot.py b/RPI Code/Arlo/examples/arlo-fullscreensnapshot.py new file mode 100644 index 0000000..09f58a3 --- /dev/null +++ b/RPI Code/Arlo/examples/arlo-fullscreensnapshot.py @@ -0,0 +1,31 @@ +from arlo import Arlo + +USERNAME = 'fransolet.thomas@gmail.com' +PASSWORD = 'Coconuts09' + +try: + # Instantiating the Arlo object automatically calls Login(), which returns an oAuth token that gets cached. + # Subsequent successful calls to login will update the oAuth token. + arlo = Arlo(USERNAME, PASSWORD) + # At this point you're logged into Arlo. + + # Get the list of devices and filter on device type to only get the basestation. + # This will return an array which includes all of the basestation's associated metadata. + basestations = arlo.GetDevices('basestation') + + # Get the list of devices and filter on device type to only get the camera. + # This will return an array which includes all of the camera's associated metadata. + cameras = arlo.GetDevices('camera') + + # Tells the Arlo basestation to trigger a snapshot on the given camera. + # This snapshot is not instantaneous, so this method waits for the response and returns the url + # for the snapshot, which is stored on the Amazon AWS servers. + snapshot_url = arlo.TriggerFullFrameSnapshot(basestations[0], cameras[0]) + + # This method requests the snapshot for the given url and writes the image data to the location specified. + # In this case, to the current directory as a file named "snapshot.jpg" + # Note: Snapshots are in .jpg format. + arlo.DownloadSnapshot(snapshot_url, 'snapshot.jpg') + +except Exception as e: + print(e) diff --git a/RPI Code/Arlo/examples/arlo-motiondetect.py b/RPI Code/Arlo/examples/arlo-motiondetect.py new file mode 100644 index 0000000..48e2bd0 --- /dev/null +++ b/RPI Code/Arlo/examples/arlo-motiondetect.py @@ -0,0 +1,32 @@ +from arlo import Arlo + +USERNAME = 'fransolet.thomas@gmail.com' +PASSWORD = 'Coconuts09' + +try: + + # Instantiating the Arlo object automatically calls Login(), which returns an oAuth token that gets cached. + # Subsequent successful calls to login will update the oAuth token. + arlo = Arlo(USERNAME, PASSWORD) + # At this point you're logged into Arlo. + + # Get the list of devices and filter on device type to only get the basestation. + # This will return an array which includes all of the basestation's associated metadata. + basestations = arlo.GetDevices('basestation') + cameras = arlo.GetDevices('camera') + # Define a callback function that will get called once for each motion event. + def callback(arlo, event): + # Here you will have access to self, basestation_id, xcloud_id, and the event schema. + print("motion event detected!") + print(event) + print(arlo) + #print("try to take snapshot") + #snapshot_url = arlo.TriggerFullFrameSnapshot(basestations[0], cameras[0]) + #arlo.DownloadSnapshot(snapshot_url, 'snapshot.jpg') + + #print(basestations) + + # Subscribe to motion events. This method blocks until the event stream is closed. (You can close the event stream in the callback if you no longer want to listen for events.) + arlo.SubscribeToMotionEvents(basestations[0], callback) +except Exception as e: + print(e) diff --git a/RPI Code/Arlo/examples/arlo-motiondetect.py.save b/RPI Code/Arlo/examples/arlo-motiondetect.py.save new file mode 100644 index 0000000..06f082f --- /dev/null +++ b/RPI Code/Arlo/examples/arlo-motiondetect.py.save @@ -0,0 +1,31 @@ +cameras = arlo.GetDevices('camera')from arlo import Arlo + +USERNAME = 'fransolet.thomas@gmail.com' +PASSWORD = 'Coconuts09' + +try: + + # Instantiating the Arlo object automatically calls Login(), which returns an oAuth token that gets cached. + # Subsequent successful calls to login will update the oAuth token. + arlo = Arlo(USERNAME, PASSWORD) + # At this point you're logged into Arlo. + + # Get the list of devices and filter on device type to only get the basestation. + # This will return an array which includes all of the basestation's associated metadata. + basestations = arlo.GetDevices('basestation') + + # Define a callback function that will get called once for each motion event. + def callback(arlo, event): + # Here you will have access to self, basestation_id, xcloud_id, and the event schema. + print("motion event detected!") + print(event) + print(arlo) + snapshot_url = arlo.TriggerFullFrameSnapshot(basestations[0], cameras[0]) + arlo.DownloadSnapshot(snapshot_url, 'snapshot.jpg') + + #print(basestations) + + # Subscribe to motion events. This method blocks until the event stream is closed. (You can close the event stream in the callback if you no longer want to listen for events.) + arlo.SubscribeToMotionEvents(basestations[0], callback) +except Exception as e: + print(e) diff --git a/RPI Code/Arlo/examples/arlo-setmode.py b/RPI Code/Arlo/examples/arlo-setmode.py new file mode 100644 index 0000000..47973c6 --- /dev/null +++ b/RPI Code/Arlo/examples/arlo-setmode.py @@ -0,0 +1,29 @@ +from arlo import Arlo + +USERNAME = 'user@example.com' +PASSWORD = 'supersecretpassword' + +try: + # Instantiating the Arlo object automatically calls Login(), which returns an oAuth token that gets cached. + # Subsequent successful calls to login will update the oAuth token. + arlo = Arlo(USERNAME, PASSWORD) + # At this point you're logged into Arlo. + + # Get the list of devices and filter on device type to only get the basestation. + # This will return an array which includes all of the basestation's associated metadata. + basestations = arlo.GetDevices('basestation') + + # Arm Arlo. + arlo.Arm(basestations[0]) + # Or + # Disarm Arlo. + # arlo.Disarm(basestations[0]) + # Or + # Change Mode to some custom mode you created. + # arlo.CustomMode(basestations[0], "mode3") # 'mode3' is the id of a custom mode you created. + # Or + # Change Mode to Schedule. + # arlo.CustomMode(basestations[0], mode=None, schedules=['schedules.1']) # 'schedules.1' is the id of my default schedule." + +except Exception as e: + print(e) diff --git a/RPI Code/Arlo/examples/arlo-snapshot.py b/RPI Code/Arlo/examples/arlo-snapshot.py new file mode 100644 index 0000000..95f3374 --- /dev/null +++ b/RPI Code/Arlo/examples/arlo-snapshot.py @@ -0,0 +1,44 @@ +from arlo import Arlo + +USERNAME = 'fransolet.thomas@gmail.com' +PASSWORD = 'Coconuts09' + +try: + # Instantiating the Arlo object automatically calls Login(), which returns an oAuth token that gets cached. + # Subsequent successful calls to login will update the oAuth token. + arlo = Arlo(USERNAME, PASSWORD) + # At this point you're logged into Arlo. + + # Get the list of devices and filter on device type to only get the basestation. + # This will return an array which includes all of the basestation's associated metadata. + basestations = arlo.GetDevices('basestation') + + # Get the list of devices and filter on device type to only get the cameras. + # This will return an array of cameras, including all of the cameras' associated metadata. + cameras = arlo.GetDevices('camera') + + # Trigger the snapshot. + url = arlo.TriggerFullFrameSnapshot(basestations[0], cameras[0]); + + # Download snapshot. + arlo.DownloadSnapshot(url, 'snapshot.jpg') + + # If you are already recording, or have a need to snapshot while recording, you can do so like this: + """ + # Starting recording with a camera. + arlo.StartRecording(basestations[0], cameras[0]); + + # Wait for 4 seconds while the camera records. (There are probably better ways to do this, but you get the idea.) + time.sleep(4) + + # Trigger the snapshot. + url = arlo.TriggerStreamSnapshot(basestations[0], cameras[0]); + + # Download snapshot. + arlo.DownloadSnapshot(url, 'snapshot.jpg') + + # Stop recording. + arlo.StopRecording(cameras[0]); + """ +except Exception as e: + print(e) diff --git a/RPI Code/Arlo/examples/arlo-streamingvideo.py b/RPI Code/Arlo/examples/arlo-streamingvideo.py new file mode 100644 index 0000000..8602b26 --- /dev/null +++ b/RPI Code/Arlo/examples/arlo-streamingvideo.py @@ -0,0 +1,39 @@ +from arlo import Arlo + +from subprocess import call + +USERNAME = 'user@example.com' +PASSWORD = 'supersecretpassword' + +try: + + # Instantiating the Arlo object automatically calls Login(), + # which returns an oAuth token that gets cached. + # Subsequent successful calls to login will update the oAuth token. + arlo = Arlo(USERNAME, PASSWORD) + # At this point you're logged into Arlo. + + # Get the list of devices and filter on device type to only get the cameras. + # This will return an array which includes all of the canera's associated metadata. + cameras = arlo.GetDevices('camera') + + # Get the list of devices and filter on device type to only get the basestation. + # This will return an array which includes all of the basestation's associated metadata. + basestations = arlo.GetDevices('basestation') + + # Send the command to start the stream and return the stream url. + url = arlo.StartStream(basestations[0], cameras[0]) + + # Record the stream to a file named 'test.mp4'. + # **Requires ffmpeg 3.4 or greater.** + # For this example, I'm going to open ffmpeg. + # Crucially important is the '-t' flag, which specifies a recording time. (See the ffmpeg documentation.) + # This is just a crude example, but hopefully it will give you some ideas. + # You can use any number of libraries to do the actual streaming. OpenCV or VLC are both good choices. + # NOTE: This will print the output of ffmpeg to STDOUT/STDERR. If you don't want that, you will + # need to pass additional arguments to handle those streams. + + call(['ffmpeg', '-re', '-i', url, '-t', '10', '-acodec', 'copy', '-vcodec', 'copy', 'test.mp4']) + +except Exception as e: + print(e) diff --git a/RPI Code/Arlo/examples/arlo-togglecamera.py b/RPI Code/Arlo/examples/arlo-togglecamera.py new file mode 100644 index 0000000..544e2b3 --- /dev/null +++ b/RPI Code/Arlo/examples/arlo-togglecamera.py @@ -0,0 +1,26 @@ +from arlo import Arlo + +USERNAME = 'user@example.com' +PASSWORD = 'supersecretpassword' + +try: + # Instantiating the Arlo object automatically calls Login(), which returns an oAuth token that gets cached. + # Subsequent successful calls to login will update the oAuth token. + arlo = Arlo(USERNAME, PASSWORD) + # At this point you're logged into Arlo. + + # Get the list of devices and filter on device type to only get the basestations. + # This will return an array of basestations, including all of the basestations' associated metadata. + basestations = arlo.GetDevices('basestation') + + # Get the list of devices and filter on device type to only get the cameras. + # This will return an array of cameras, including all of the cameras' associated metadata. + cameras = arlo.GetDevices('camera') + + # Turn camera on. + print(arlo.ToggleCamera(basestations[0], cameras[0], True)) + # Turn camera off. + #print(arlo.ToggleCamera(basestations[0], cameras[0], False)) + +except Exception as e: + print(e) diff --git a/RPI Code/Arlo/examples/arlobaby-audiocontrol.py b/RPI Code/Arlo/examples/arlobaby-audiocontrol.py new file mode 100644 index 0000000..a6bd119 --- /dev/null +++ b/RPI Code/Arlo/examples/arlobaby-audiocontrol.py @@ -0,0 +1,39 @@ +from arlo import Arlo + +USERNAME = 'user@example.com' +PASSWORD = 'supersecretpassword' + +try: + # Instantiating the Arlo object automatically calls Login(), which returns an oAuth token that gets cached. + # Subsequent successful calls to login will update the oAuth token. + arlo = Arlo(USERNAME, PASSWORD) + # At this point you're logged into Arlo. + + # Get the list of devices and filter on device type to only get the basestation. + # This will return an array which includes all of the basestation's associated metadata. + basestations = arlo.GetDevices('basestation') + + # Get the list of devices and filter on device type to only get the camera. + # This will return an array which includes all of the camera's associated metadata. + cameras = arlo.GetDevices('camera') + + # Get current state payload + arlo.GetAudioPlayback(cameras[0]) + + # Start playing + arlo.PlayTrack(cameras[0], track_id, position) + + # Pause the track + arlo.PauseTrack(cameras[0]) + + # Skip the track + arlo.SkipTrack(cameras[0]) + + # Set the sleep timer + arlo.SetSleepTimerOn(cameras[0]) + + # Set the playback mode ot continuous + arlo.SetLoopBackModeContinuous(cameras[0]) + +except Exception as e: + print(e) diff --git a/RPI Code/Arlo/examples/arlobaby-nightlightcontrol.py b/RPI Code/Arlo/examples/arlobaby-nightlightcontrol.py new file mode 100644 index 0000000..8adda23 --- /dev/null +++ b/RPI Code/Arlo/examples/arlobaby-nightlightcontrol.py @@ -0,0 +1,34 @@ +from arlo import Arlo + +USERNAME = 'user@example.com' +PASSWORD = 'supersecretpassword' + +try: + # Instantiating the Arlo object automatically calls Login(), which returns an oAuth token that gets cached. + # Subsequent successful calls to login will update the oAuth token. + arlo = Arlo(USERNAME, PASSWORD) + # At this point you're logged into Arlo. + + # Get the list of devices and filter on device type to only get the basestation. + # This will return an array which includes all of the basestation's associated metadata. + basestations = arlo.GetDevices('basestation') + + # Get the list of devices and filter on device type to only get the camera. + # This will return an array which includes all of the camera's associated metadata. + cameras = arlo.GetDevices('camera') + + # Get current state payload + state=arlo.GetCameraState(cameras[0]) + print(state["properties"][0]["nightLight"]) + + # night light on + arlo.SetNightLightOn(cameras[0]) + + # night light timer on + arlo.SetNightLightTimerOn(cameras[0], 500) + + # night light color mode + arlo.SetNightLightMode(cameras[0], mode={"blue":255,"green":255,"red":255 }) # or mode="rainbow" + +except Exception as e: + print(e) diff --git a/RPI Code/Arlo/examples/arlobaby-tempcontrol.py b/RPI Code/Arlo/examples/arlobaby-tempcontrol.py new file mode 100644 index 0000000..92ae76e --- /dev/null +++ b/RPI Code/Arlo/examples/arlobaby-tempcontrol.py @@ -0,0 +1,36 @@ +from arlo import Arlo + +USERNAME = 'user@example.com' +PASSWORD = 'supersecretpassword' + +try: + # Instantiating the Arlo object automatically calls Login(), which returns an oAuth token that gets cached. + # Subsequent successful calls to login will update the oAuth token. + arlo = Arlo(USERNAME, PASSWORD) + # At this point you're logged into Arlo. + + # Get the list of devices and filter on device type to only get the basestation. + # This will return an array which includes all of the basestation's associated metadata. + basestations = arlo.GetDevices('basestation') + + # Get the list of devices and filter on device type to only get the camera. + # This will return an array which includes all of the camera's associated metadata. + cameras = arlo.GetDevices('camera') + + # Turn temperature alerts on + arlo.TempAlertOn(cameras[0]) + + # Alert min threshold (so if temp falls below this number it alerts) + arlo.SetTempAlertThresholdMin(cameras[0], 170) + + # Alert max threshold (so if temp go above this number it alerts) + arlo.SetTempAlertThresholdMax(cameras[0], 270) + + # record temperature history + arlo.TempRecordingOn(cameras[0]) + + # Set the temperature unit to Celcius + arlo.SetTempUnit(cameras[0]["uniqueId"], "C") + +except Exception as e: + print(e) diff --git a/RPI Code/Arlo/logo.png b/RPI Code/Arlo/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..d33a19515a1d75cb6d8371cf7e752edfb1ee690a GIT binary patch literal 10292 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

    >s>yJ;0<$J0$E`e7Hl|{UaihJ9eUTg_+!9Y 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