Note 1: See my write-up on Home Security Automation which provides an architectural overview of how this project works with others in my collection.
Note 2: While I use the word Automation in these projects, there is no integration with sensible frameworks like openHAB or Home Assistant... not yet at least. The goal behind this project was a learning opportunity by employing some specific technologies and opinion on design. The parts you'll most likely find useful are touch-points with third-party libraries like Flask, ZeroMQ and RabbitMQ, because seamless behavior comes after much trial and error.
This project supports fetching image snapshots from Foscam IP cameras and forwards this data to companion projects using a RabbitMQ exchange. Most IP cameras will have varying degrees of "documented" methods to fetch image data on-demand. My use of Foscam is really based on the hardware that I currently use (at the time of writing, some of their web sites appear broken 🤷).
Additional features include:
- a self-managed local cache of snapshot images which are asynchronously uploaded to
CloudStorage
instances which currently includes Google Drive. - periodic management of Google Driver folders by organizing non-starred images into year-month-day folders.
- a local FTP server to support FTP-based IP camera image push using FTP basic-auth.
- object detection using AWS Rekognition with metadata-flags for human detection.
This application extends my own boilerplate application hosted in docker hub and takes its own git submodule dependency on my own package.
There are also a few branches of this project, made for various platforms, one of which was to support the Jetson Nano developer kit. This branch includes logic to control the PWM fan to run when detection is being done. The results weren't perfect so if you do intend to fork this branch, your mileage may vary. The main branch of this project works with AWS Rekognition and I've found it to be fast, rather accurate and returns useful metadata for person detection. The current logic makes use of the DetectLabels
feature and carries associated pricing.
The diagrams below show both the class inheritance structure. Here is the relationship between this project and my pylib submodule. For brevity, not everything is included such as the cloud storage management classes, but those are mostly self-contained. These are the non-obvious relationships.
Snapshot
receives trigger messages and fetches image data from cameras and forwards this toObjectDetector
which delegates to the chosen detection mechanism to identify features in the images. Both of these are descendants ofZMQRelay
for inter-thread message passing. You can see this flow illustrated in the diagram below.- The
main
application thread contains an instance ofZMQListener
which receives messages from the configured exchange and forwards them toSnapshot
. After object detection, outgoing messages are sent viaRabbitMQRelay
.
See the diagram below for an example about how ZeroMQ is used as a message relay between threads.
ZMQListener
is responsible for receiving RabbitMQ messages from the network and then forwards these through a chain ofZMQRelay
instances out the way back to the RabbitMQ publisher.
Technologies that help make this project useful:
Also:
Here is some detail about the intended use of this package.
Beyond the Python dependencies defined in the Poetry configuration, the project carries hardcoded dependencies on Sentry and 1Password in order to function.
-
🛑 This project uses 1Password Secrets Automation to store both application key-value pairs as well as runtime secrets. It is assumed that the connect server containers are already running on your environment. If you do not want to use this, then you'll need to fork this package and make the changes as appropriate. It's actually very easy to set up, but note that 1Password is a paid product with a free-tier for secrets automation.
-
🚧 If a Google authorization token is not present on the local file system, pydrive will initiate an oauth workflow which needs to be followed at least once in order to interact with Google Drive. During this flow, a URL will be logged that needs to be followed by the authorizing user at which point the client will store the token and it will be backed up regularly. As long as the token is backed up, it will remain valid until the authorization is revoked.
Here is an example of how this looks for my application and the generation of the docker-compose.yml relies on this step. Your secrets automation vault must contain an entry called ENV.snapshot-processor
with these keys:
Variable | Description | Example |
---|---|---|
APP_NAME |
Application name used in logging and metrics | snapshot-processor |
AWS_ALT_REGION |
AWS region (used for dual-region) | eu-west-1 |
AWS_CONFIG_FILE |
AWS client configuration file | /home/app/.aws/config |
AWS_DEFAULT_REGION |
AWS region | us-east-1 |
CRONITOR_MONITOR_KEY |
Cronitor configuration key | project specific |
DEVICE_NAME |
Used for container host name. | snapshot-processor |
FTP_CREATE_DIRS |
IP-camera upload directories | snapshots/cam1,snapshots/cam2 |
FTP_PASS |
FTP basic-auth password | project specific |
FTP_UPLOAD_DIR |
Upload directory within the FTP root | uploads/snapshots |
FTP_USER |
FTP basic-auth user | project specific |
GOOGLE_DRIVE_FOLDER |
Google Drive folder name | project specific |
HC_PING_URL |
Healthchecks URL | project specific |
INPUT_X_LOCATION |
Device location | project specific |
INPUT_X_TYPE |
Device type | Camera |
OBJECT_DETECTION_ENABLED |
Detect objects in snapshots | true |
OP_CONNECT_HOST |
1Password connect server URL | network specific |
OP_CONNECT_TOKEN |
1Password connect server token | project specific |
OP_VAULT |
1Password vault | project specific |
OUTPUT_X_LOCATION |
Device location | project specific |
OUTPUT_X_TYPE |
Device type | Camera |
RABBITMQ_DEVICE_TOPIC |
Publication topic for this project | snapshot |
RABBITMQ_EXCHANGE |
Name of RabbitMQ exchange | home_automation |
RABBITMQ_SERVER_ADDRESS |
IP address of RabbitMQ exchange | network specific |
USER |
Process user | app |
With these configured, you are now able to build the application. Any variables referenced in the application configuration will be automatically replaced.
In addition to this, additional runtime configuration is used by the application, and also need to be contained within the secrets vault. With these configured, you are now able to run the application.
- Clone the repo
git clone https://github.com/tailucas/snapshot-processor.git
- Verify that the git submodule is present.
git submodule init git submodule update
- Make the Docker runtime user and set directory permissions. ✋ Be sure to first review the Makefile contents for assumptions around user IDs for Docker.
make user
- Now generate the docker-compose.yml:
make setup
- And generate the Docker image:
make build
- If successful and the local environment is running the 1Password connect containers, run the application. For foreground:
For background:
make run
make rund
Running the application will:
- Start the RabbitMQ client.
- Start a watchdog file system observer to detect FTP uploads.
- Start a Google Drive client to store snapshots with date-folder archive function.
- Start the main application loop
Snapshot
which waits for trigger events.
Distributed under the MIT License. See LICENSE for more information.