Giter Club home page Giter Club logo

alexa-ip-cam's Introduction

IMPORTANT NOTE: At the current time some Amazon devices may not work with this project because they don't properly manage the recent Let's Encrypt root certification expiration. I am working with Amazon to fix things on their side but in the meantime the steps described below should work with the majority of Amazon devices.

alexa-ip-cam

Use Alexa's Smart Home Skill API with standalone IP cameras to stream live video and recorded video to an Alexa device without needing any camera cloud service.

Background

Many people like myself have IP cameras without a cloud service that perhaps they'd like to control using Amazon's Alexa Smart Home Skill API. This API is great but assumes you are using a cloud service for your cameras and has very specific security and streaming requirements that makes it challenging to connect standalone cameras. In my case I have several Axis IP cameras around the house connected via a LAN to a local Linux machine and are configured to do motion detection and store recordings on the Linux box. I also use Zoneminder as a Network Video Recorder and its mobile companion app zmNinja. No camera cloud service is needed in my system which avoids associated recurring costs BUT the system is not Alexa compatible "out of the box".

So I created the alexa-ip-camera project to solve this problem and enable me to view my home's live and recorded camera streams on Amazon devices such as the Echo Show, the Echo Spot and the FireTV. To do that I had to develop an Alexa Smart Home skill plus some supporting components running on the local Linux machine.

Note: Please see my related smart-zoneminder project that enables fast upload of ZoneMinder alarm frame images to an S3 archive where they are analyzed by Amazon Rekognition or locally by Tensorflow and made accessible by voice via Alexa.

I hope others find this useful. I've described the project in some detail and outlined the steps below that I used to create this skill.

System Architecture

The system consists of the following main components.

  1. An Alexa Smart Home skill.
  2. An AWS Lambda instance for handling the skill intents including camera discovery and control.
  3. An Alexa-enabled device with a display such as Amazon Echo Show or Spot.
  4. A RTSP proxy running on the local linux machine that aggregates the streams from the cameras on the LAN into one front-end stream. This component isn't needed if you only have one camera. I used The LIVE555 Proxy Server.
  5. A TLS encryption proxy on the local linux machine that encypts the stream from the RTSP proxy server and streams it on local machine's port 443. I used stunnel.
  6. A program running on the local linux machine that uploads camera recording metadata to the Alexa Event Gateway via the Alexa.MediaMetadata Interface to enable the viewing of past events captured by the camera. In my case I used a node.js app I created called process-events.js.
  7. A webserver running on the local linux machine that allows the Lambda instance to access the recordings stored by the cameras. I'm using Apache.

Prerequisites

You'll need the following setup before starting this project.

  1. An Amazon Developers account.
  2. An Amazon AWS account.
  3. IP camera(s) connected to your LAN that support streaming over RTSP and local recordings.
  4. A Linux machine connected to your LAN. I used an existing server running Ubuntu 18.04 but a Raspberry Pi, for example, would be fine.

Installation Steps

Clone this repo

Configure General Settings

Copy config-template.json to a file called config.json. There are several values in that file that need to be changed to suit your setup. Some of them are described below.

Setup the Alexa Smart Home Skill and and Lambda handler

The Steps to Build a Smart Home Skill and Build Smart Home Camera Skills on the Amazon Alexa Developers site give detailed instructions on how to create the skill and how the API works. Replace the Lambda code in the template example with the code in index.js in lambda directory of this repo. The code emulates the camera configuration data that would normally come from a 3rd party camera cloud service. You'll have to edit config.json to make it reflect your camera names and specs.

Setup the RTSP Proxy

emtunc's blog provides excellent instructions on how to setup the proxy from Live555. I needed to set OutPacketBuffer::maxSize to 400000 bytes in live555ProxyServer.cpp to stop the feed from getting truncated. I didn't make the other changes that emtunc made (port and stream naming).

The RTSP proxy needs to be on a different port than the individual streams. In my case the proxy port is 8554 since the cameras have their RTSP port set to 554. The proxy is therefore started with -p 8554 on the command line. You have to make sure nothing else is using that port on the server running the proxy.

The proxy-start script is run as a cronjob as root at boot to start the RTSP proxy. The cronjob is delayed by 60 secs to allow networking to come up first.

Setup DNS and SSL certs

I followed the corresponding steps in CameraPi almost exactly except I'm using GoDaddy to manage domains and DNS instead of AWS Route 53. Note: Let’s Encrypt CA issues short-lived certificates (90 days). Make sure you renew the certificates at least once in 3 months.

At the current time you should configure your ACME client to use the alternative chain instead of the default chain when requesting a cert from Let's Encrypt because this allows most Amazon devices to properly handle Let's Encrypt's recent root cert expiration. For more information on this issue please see Old Let’s Encrypt Root Certificate Expiration and OpenSSL 1.0.2. I use certbot as an ACME client and the command to do so is shown below.

$ certbot --version
certbot 1.21.0
$ sudo certbot -d URI --rsa-key-size 4096 --manual \
--preferred-challenges dns \
--preferred-chain "ISRG Root X1" certonly

Per the Alexa Smart Home camera documentation you can provide the API a local or remote camera URI. I'm currently providing a local URI but did try remote as well since I was a little concerned about putting a private IP address in a DNS record. But local results in lower latency over remote but its not a lot, only about 500 ms and I didn't have to open a port to the Internet in my firewall. The biggest drawback is that I won't be able to view my cameras on an Echo device outside my home, for example at work.

Setup the TLS encryption proxy

stunnel is a ubuntu package so it easy to install using apt-get as root. The configuration I used is in the file stunnel.conf which is placed in /etc/stunnel/stunnel.conf on my machine. stunnel is run as a cronjob as root at boot to start it. The cronjob is delayed by 60 secs to allow networking to come up first.

Setup Camera

I created a user for Alexa access and a streaming profile for each camera. The settings for the profile are shown in the table below. Note the specific settings. These are the only values that have been tested so you should use the same or be prepared to experiment.

Parameter Value Units
Resolution 1280x720 pixels
Encoder Type H.264 NA
Encoder Compression 30 NA
Encoder Max Frame Rate Unlimited NA
Encoder GOP 62 frames
Encoder Profile Baseline NA
Encoder Bit Rate Control Variable NA

Setup Camera Local Recording and Processing

Most modern IP cameras allow you to store a recording to a local drive triggered from motion detection or another event. This needs to be enabled to use the Alexa Cameras Recap API which allows you to view those recordings.

You'll need to change config.json to point the process-events.js app that processes the recordings and most likely the app itself to suit the particular way your camera stores recordings. The code here has only been tested against Axis cameras. The process-events.js app is run as a Linux service using systemd.

Authenticate Yourself to Alexa with Permissions

As mentioned above the recording metadata is sent to the Alexa Gateway. This is done asynchronously and so you must provide the proper authentication information with the request. Follow the steps outlined in Authenticate a Customer to Alexa with Permissions to make this happen. You'll also need to add the relevant information to config.json.

Also see Authorization Code Grant, Alexa.Authorization Interface and Send Events to the Event Gateway for more information on how this works.

Setup Webserver

A webserver is required to serve up the camera recordings to the skill's lambda function running in the AWS cloud. I'm using Apache and for this purpose. I just created a virtual host that pointed to the directory where the cameras store their recordings. Note that Alexa Smart Home API requires this connection to be over https and self-signed certs may not be used as outlined above in the SSL cert section.

Operation

Once everything is setup you need to enable your skill in the Alexa companion mobile app or web app. Then ask Alexa to "discover devices" and your camera(s) should be found, Alexa will tell you that and they'll be visible in the app. After that just ask Alexa to "show front porch camera" (or what every you named them) and the camera video will be streamed to your Echo device with a screen. Or you can say "Alexa, show the event that just happened at the front porch camera" to see the last recorded event.

Results

Overall the skill works well but the latency between asking Alexa to show a camera and the video appearing on the Echo's or FireTV screen is a little too long for a great experience, on average 3 secs or so. I haven't yet tracked down the cause of it.

Also I've seen the video re-buffer occasionally which can be irritating and once in a great while the video freezes during rebuffering. I've found that the camera settings above minimize the buffering across all the Amazon Alexa devices I've tested. I think the source of the buffering is the video decode time in the device which varies across device type due to hardware capabilities. Since the the video is delivered to the device from the camera via TCP (stunnel uses SSL over TCP) the network will not let the device discard packets when gets it gets behind in its decoding. I don't know a way to use stunnel with UDP which would obviate this issue. The table below shows the device types I've tested and video quality performance.

Device Buffering Frequency Note
Fire TV Cube Never Expected since the device is optimized for video.
Fire TV Stick 4K Never Expected since the device is optimized for video.
Echo Show Gen 1 Rarely
Echo Show Gen 2 Rarely
Fire HD 10 Tablet Occasionally Expected given its hardware capabilities.

Acknowledgments

I looked for other projects on GitHub for code to leverage but didn't find anything exactly solving my particular problem. However I did find an excellent repo called CameraPi from Sam Machin that describes how to use Alexa to control a camera connected to a Raspberry PI that I used as a basis for my effort. Thank you Sam!

I used emtunc's very cool blog to learn how to setup the RTSP proxy. Thank you emtunc!

alexa-ip-cam's People

Contributors

goruck avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

alexa-ip-cam's Issues

Live555 blog link dead

The readme.md has:

Setup the RTSP Proxy

emtunc's blog provides excellent instructions on how to setup the proxy from Live555.

But while not technically a dead link, it looks like the content of how to configure Live555 is gone. Know of any other good references besides the Live555 source/web page?

Why did you need a rtsp proxy?

I'm very intrigued by this project. I use zoneminder for my NVR and was look to to solve the same problem as you (Show my LAN Cameras on my Echo Show).

However I'm a bit confused on why you need live555 at all.

Can't you just add your individual rtsp streams to the camera.json?

stunnel appears to be closing the connection after a couple seconds

When I try and view the camera feed on my Fire HD 8, it times out after ~10-15 seconds, give or take. I'm pretty sure I've configured everything properly. Stunnel certs are in the right place.

Looking at the stunnel logs, I can see the Fire making the connection, but then it seems to close it pretty quickly after that. Here is the start of the logs, including the initial connection:

2018.11.20 21:25:23 LOG7[ui]: Clients allowed=500
2018.11.20 21:25:23 LOG5[ui]: stunnel 5.30 on x86_64-pc-linux-gnu platform
2018.11.20 21:25:23 LOG5[ui]: Compiled/running with OpenSSL 1.0.2g  1 Mar 2016
2018.11.20 21:25:23 LOG7[cron]: Cron thread initialized
2018.11.20 21:25:23 LOG5[ui]: Threading:PTHREAD Sockets:POLL,IPv6,SYSTEMD TLS:ENGINE,FIPS,OCSP,PSK,SNI Auth:LIBWRAP
2018.11.20 21:25:23 LOG7[ui]: errno: (*__errno_location ())
2018.11.20 21:25:23 LOG5[ui]: Reading configuration from file /etc/stunnel/stunnel.conf
2018.11.20 21:25:23 LOG5[ui]: UTF-8 byte order mark not detected
2018.11.20 21:25:23 LOG5[ui]: FIPS mode disabled
2018.11.20 21:25:23 LOG7[ui]: Compression disabled
2018.11.20 21:25:23 LOG7[ui]: PRNG seeded successfully
2018.11.20 21:25:23 LOG6[ui]: Initializing service [rtsp]
2018.11.20 21:25:23 LOG6[ui]: Loading certificate from file: /etc/stunnel/fullchain.pem
2018.11.20 21:25:23 LOG6[ui]: Certificate loaded from file: /etc/stunnel/fullchain.pem
2018.11.20 21:25:23 LOG6[ui]: Loading private key from file: /etc/stunnel/privkey.pem
2018.11.20 21:25:23 LOG6[ui]: Private key loaded from file: /etc/stunnel/privkey.pem
2018.11.20 21:25:23 LOG7[ui]: Private key check succeeded
2018.11.20 21:25:23 LOG7[ui]: DH initialization
2018.11.20 21:25:23 LOG7[ui]: Could not load DH parameters from /etc/stunnel/fullchain.pem
2018.11.20 21:25:23 LOG6[ui]: Using dynamic DH parameters
2018.11.20 21:25:23 LOG7[ui]: ECDH initialization
2018.11.20 21:25:23 LOG7[ui]: ECDH initialized with curve prime256v1
2018.11.20 21:25:23 LOG7[ui]: SSL options: 0x03004004 (+0x03004000, -0x00000000)
2018.11.20 21:25:23 LOG5[ui]: Configuration successful
2018.11.20 21:25:23 LOG7[ui]: Listening file descriptor created (FD=7)
2018.11.20 21:25:23 LOG7[ui]: Service [rtsp] (FD=7) bound to 0.0.0.0:443
2018.11.20 21:25:23 LOG7[ui]: Created pid file /var/run/stunnel4.pid
2018.11.20 21:26:23 LOG6[cron]: Executing cron jobs
2018.11.20 21:26:23 LOG5[cron]: Updating DH parameters
2018.11.20 21:26:49 LOG5[cron]: DH parameters updated
2018.11.20 21:26:49 LOG6[cron]: Cron jobs completed in 26 seconds
2018.11.20 21:26:49 LOG7[cron]: Waiting 86374 seconds
2018.11.20 21:29:00 LOG7[ui]: Found 1 ready file descriptor(s)
2018.11.20 21:29:00 LOG7[ui]: FD=4 events=0x2001 revents=0x0
2018.11.20 21:29:00 LOG7[ui]: FD=7 events=0x2001 revents=0x1
2018.11.20 21:29:00 LOG7[ui]: Service [rtsp] accepted (FD=3) from 192.168.1.66:54508
2018.11.20 21:29:00 LOG7[0]: Service [rtsp] started
2018.11.20 21:29:00 LOG5[0]: Service [rtsp] accepted connection from 192.168.1.66:54508
2018.11.20 21:29:00 LOG7[0]: SSL state (accept): before/accept initialization
2018.11.20 21:29:00 LOG7[0]: SNI: no virtual services defined
2018.11.20 21:29:00 LOG7[0]: New session callback
2018.11.20 21:29:00 LOG7[0]:      1 server accept(s) requested
2018.11.20 21:29:00 LOG7[0]:      1 server accept(s) succeeded
2018.11.20 21:29:00 LOG7[0]:      0 server renegotiation(s) requested
2018.11.20 21:29:00 LOG7[0]:      0 session reuse(s)
2018.11.20 21:29:00 LOG7[0]:      0 internal session cache item(s)
2018.11.20 21:29:00 LOG7[0]:      0 internal session cache fill-up(s)
2018.11.20 21:29:00 LOG7[0]:      0 internal session cache miss(es)
2018.11.20 21:29:00 LOG7[0]:      0 external session cache hit(s)
2018.11.20 21:29:00 LOG7[0]:      0 expired session(s) retrieved
2018.11.20 21:29:00 LOG6[0]: SSL accepted: new session negotiated
2018.11.20 21:29:00 LOG6[0]: No peer certificate received
2018.11.20 21:29:00 LOG6[0]: Negotiated TLSv1.2 ciphersuite ECDHE-RSA-AES256-GCM-SHA384 (256-bit encryption)
2018.11.20 21:29:00 LOG7[0]: Compression: null, expansion: null
2018.11.20 21:29:00 LOG6[0]: s_connect: connecting 192.168.1.13:554
2018.11.20 21:29:00 LOG7[0]: s_connect: s_poll_wait 192.168.1.13:554: waiting 10 seconds
2018.11.20 21:29:00 LOG5[0]: s_connect: connected 192.168.1.13:554
2018.11.20 21:29:00 LOG6[0]: persistence: 192.168.1.13:554 cached
2018.11.20 21:29:00 LOG5[0]: Service [rtsp] connected remote server from 192.168.1.8:51646
2018.11.20 21:29:00 LOG7[0]: Remote descriptor (FD=9) initialized
2018.11.20 21:29:00 LOG6[0]: SSL socket closed (SSL_read)
2018.11.20 21:29:00 LOG7[0]: Sent socket write shutdown
2018.11.20 21:29:00 LOG5[0]: Connection closed: 139 byte(s) sent to SSL, 163 byte(s) sent to socket
2018.11.20 21:29:00 LOG7[0]: Remote descriptor (FD=9) closed
2018.11.20 21:29:00 LOG7[0]: Local descriptor (FD=3) closed
2018.11.20 21:29:00 LOG7[0]: Service [rtsp] finished (0 left)

I can connect to port 554 via VLC (with authentication) just fine. I'm not using live555ProxyServer to start with for simplicity.

Any thoughts here?

Problem with stunnel

Hi there,

good guide so far but i'm stuck with stunnel
Log tells me service started and connection is accepted but is then reseted because of error:140760FC:SSL routines:SSL23_GET_CLIENT_HELLO:unknown protocol - tested with VLC

If connection is made directly to live555proxy stream works

Can this work using ip reservation and port forwarding intead of static ip?

I have spectrum internet and residential customers cannot get static ip's. Their only option available is to use ip reservation and port forwarding. Can this skill work in that environment? If so, could you provide assistance in setting it up or suggest a few links that might provide some assistance?

Issue with LetsEncrypt expiration

I am using Echo Show 10 (3rd Gen). Sometimes back, out of the blue, Alexa couldn't connect to my RTSP feed. I wonder if this is related to the issue that you described on the home page. Am hoping I can create an issue here, just so to keep track of when issue is resolved. Thank you for the good work! =)

video plays for about a second or two, then buffers indefinitely

Sorry to inundate with tickets, but thought maybe you've seen this one. Per my other ticket, I have gotten the full setup working, at least as far as all the functional pieces are concerned. I can get video to display on my Fire, but only for a second or two before it starts buffering indefinitely.

Here are the stunnel logs for what happens when I stop the video.

What I can't quite figure out how to read is the 'Connection closed' line. Is it saying that stunnel received ~21MB from the camera, but only sent less than 1K to the tablet?

2018.12.01 09:52:19 LOG6[2]: TLSv1.2 ciphersuite: ECDHE-RSA-AES256-GCM-SHA384 (256-bit encryption)
2018.12.01 09:52:19 LOG7[2]: Compression: null, expansion: null
2018.12.01 09:52:19 LOG6[2]: s_connect: connecting 127.0.0.1:554
2018.12.01 09:52:19 LOG7[2]: s_connect: s_poll_wait 127.0.0.1:554: waiting 10 seconds
2018.12.01 09:52:19 LOG5[2]: s_connect: connected 127.0.0.1:554
2018.12.01 09:52:19 LOG6[2]: persistence: 127.0.0.1:554 cached
2018.12.01 09:52:19 LOG5[2]: Service [rtsp] connected remote server from 127.0.0.1:54790
2018.12.01 09:52:19 LOG7[2]: Setting remote socket options (FD=11)
2018.12.01 09:52:19 LOG7[2]: Option TCP_NODELAY set on remote socket
2018.12.01 09:52:19 LOG7[2]: Remote descriptor (FD=11) initialized
2018.12.01 09:54:21 LOG6[2]: TLS socket closed (SSL_read)
2018.12.01 09:54:21 LOG7[2]: Sent socket write shutdown
2018.12.01 09:54:21 LOG5[2]: Connection closed: 21099067 byte(s) sent to TLS, 984 byte(s) sent to socket
2018.12.01 09:54:21 LOG7[2]: Remote descriptor (FD=11) closed
2018.12.01 09:54:21 LOG7[2]: Local descriptor (FD=3) closed
2018.12.01 09:54:21 LOG7[2]: Service [rtsp] finished (0 left)

Alexsa Account Linking

How do you handle the account linking required by Amazon's Camera Skill? It looks like it needs an OAUTH 2.0 endpoint. Thoughts?

have you converted Zoneminder mjpeg to RTSP for this project?

I too am a Zoneminder user but with numerous analog cameras hooked through a capture card and other older cameras which do not support direct RTSP. Might you have done any investigation of converting the Zoneminder mjpeg stream into RTSP with ffmpeg/ffserver/avconv or anything else? I've found a few general posts on mjpeg to rtsp conversion, but nothing specific to Zoneminder. Thanks in advance.

Noob Account linking problems

I'll start by apologising - sorry i'm a complete noob when it comes to anysort of programming!
I've managed to create the Amazon skill but i'm hitting problems when I try to setup the account linking, i've got the following entered;
Authorisation URL: https://www.amazon.com/ap/oa
Ticket access URL: https://api.amazon.com/auth/o2/token
Client ID: my client ID created in the Login with Amazon portal
Client Secret: the secret created in the Login with Amazon portal
Client Authentication Scheme: HTTP Basic
No Scope defined
No domains defined
No default token expiration defined

When I try to Save it I get "Account linking information is not valid."

Any pointers on where I can look to try and sort this out?
Any help much appreciated
Dave

domain and TLS certs problem

Hi,
I noticed 2 prerequisites.
It means that each camera needs to have its own separate domain name and certificate?
or,
One domain name is enough and use subdomain identify different cameras?
Live555's proxy address format is rtsp://domain:443:/proxyStream-1 , .../proxyStream-2
I see that "officecamerapi.dev-cloud.co.uk:443/h264, rtsp://hallcamerapi.dev-cloud.co.uk:443/h264"
Are u have 2 live555 process to proxy 2 camera rtsp feed?

MongoDB

In your file config.json.

there is a lot field where is : REPLACE

for lot of i have the value, but i see mongodb ?
in your tutorial there never mentionned a mongoDB server.

can you indicate me if i miss a step

Alexa skill not discovering new devices

I've set everything up following your instructions and got the Skill installed for Alexa but when I try to discover new devices it doesn't find anything.

I have a pi setup running the live555 proxy and stunnel which all talk to my actual IP camera which I've also created the SSL cert for etc and my Skill and Lambda function all appear to be correct.
What troubleshooting can I do for the Alexa part? I'm very new to this side of things unfortunately so I don't know where I could have gone wrong.
Happy to provide logs/config outputs etc for additional help.

Thanks

ALL CAMERAS ARE NOT RESPONDING!

Hello,
i used your Code as a skill for a few weeks (3 cams).
Since March, 22 Amazon has changed something.
now if I call "show door" Alexa says "Camera is not responding". In German "Die Kamera reagiert gerade nicht"
On 2 Forums i could find the same Problems from Users have other Skills eg "Monocle".
Cams that are compatible with Alexa, don't have the Problem...
Read this
https://forums.developer.amazon.com/questions/140767/help-all-cameras-are-not-responding-was-working-fi.html

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.