Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
6c6847e
Update dependencies in requirements.txt
randy-seng May 22, 2025
8bf643d
Add update_precommit script
randy-seng May 22, 2025
d072bb1
Sort entries in requirements.txt
randy-seng May 22, 2025
4575a32
Pin dependencies in requirements-dev.txt
randy-seng May 22, 2025
42ee4e9
Add missing newline in LED module docstring
randy-seng May 22, 2025
24585fe
Sort import
randy-seng May 22, 2025
67d9730
Add pyproject.toml
randy-seng May 22, 2025
70558c5
Remove not needed setup.py and setup.cfg
randy-seng May 22, 2025
bfa3659
Add pre-commit config file
randy-seng May 22, 2025
d4e11d4
Add type hints to functions and improve code consistency
randy-seng May 23, 2025
0ddea5b
Sort imports
randy-seng May 23, 2025
6f2098c
Lower Python version requirement and add mypy overrides.
randy-seng May 23, 2025
e91616e
Refactor type hints and update dependencies.
randy-seng May 23, 2025
56c6653
Sort requirements-dev.txt
randy-seng May 23, 2025
2bc9390
Fix log file sorting functionality to handle files without timestamp …
randy-seng May 23, 2025
4231f83
Refactor type hints and fix import formatting.
randy-seng May 23, 2025
0b73f35
Update docstring format in `camera.py` for improved readability and c…
randy-seng Jun 17, 2025
919a1f7
Introduce Camera interface to pave the way for new camera module
randy-seng Jun 17, 2025
dd271a8
Refactor existing picamerax's PiCamera class to own PiCameraX impleme…
randy-seng Jun 17, 2025
b0764c7
Define initial version constant for OTCamera module.
randy-seng Jun 17, 2025
bd8ef1d
Move Singleton definition to abstraction layer
randy-seng Jun 17, 2025
204c281
Rename `Camera` to `CameraController` and update references across th…
randy-seng Jun 17, 2025
f57b9cc
Introduce `CameraProvider` for dynamic camera type management and ref…
randy-seng Jun 18, 2025
32f7143
Update linter configuration to use `.flake8` and `pyproject.toml`; ad…
randy-seng Jun 25, 2025
34c27b4
Sort import
randy-seng Jun 25, 2025
2ddca8f
Update super linter to v7
randy-seng Jun 25, 2025
12b486e
Try fixing differing behavior of isort in super-linter
randy-seng Jun 25, 2025
13a86cb
Format imports
randy-seng Jun 25, 2025
4e1159a
Lint sh-files
randy-seng Jun 25, 2025
e1d5a85
Lint template.html
randy-seng Jun 25, 2025
429a714
Fix lint errors in usb_flash_drive_copy.py
randy-seng Jun 25, 2025
c95108b
Omit pyink in super-linter since flake8 is already used as linter
randy-seng Jun 25, 2025
a34fe5b
Fix lint error in pre-commit config
randy-seng Jun 25, 2025
21ccf9e
Make wifistart executable to fix lint error
randy-seng Jun 25, 2025
ae1acbe
Set permissions for linter GitHub workflow
randy-seng Jun 25, 2025
dd25314
Fix lint errors in install_otcamera.sh
randy-seng Jun 25, 2025
bf48323
Add custom scalar style override to enforce double quotes in YAML ser…
randy-seng Jul 22, 2025
8ef26e2
Add install scripts
randy-seng Jul 22, 2025
e8d342b
Add .editorconfig to standardize code style
randy-seng Jul 22, 2025
2358f3e
Indent template.html content to follow consistent code style
randy-seng Jul 22, 2025
9ca2dd7
Fix indentation
randy-seng Jul 22, 2025
6a4872b
Replace tabs with spaces
randy-seng Jul 22, 2025
834e404
Merge branch 'master' into feature/7975-update-library-to-be-compatib…
randy-seng Jan 27, 2026
dbab94b
OP#7975: Cleanup merge conflicts
randy-seng Jan 27, 2026
84727a8
OP#7975: Add `CameraClosedError` exception class to handle specific c…
randy-seng Jan 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# EditorConfig is awesome: http://EditorConfig.org
# top-most EditorConfig file
root = true

# Unix-style newlines at the bottom of every file
[*]
charset = utf-8

# Tab indentation
indent_style = space
indent_size = 4

[*.{md,sh,yaml,yml}]
indent_size = 2
7 changes: 6 additions & 1 deletion .flake8
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
[flake8]
max-line-length = 88
max-line-length=88
docstring-convention=google
extend-ignore=E203
exclude=
venv
.venv
73 changes: 41 additions & 32 deletions .github/workflows/linter.yml
Original file line number Diff line number Diff line change
@@ -1,32 +1,41 @@
---
name: super-linter
#
# Documentation:
# https://help.github.com/en/articles/workflow-syntax-for-github-actions
#

on: pull_request

jobs:
build:
name: lint
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Lint Code Base
uses: docker://github/super-linter:v4
env:
VALIDATE_ALL_CODEBASE: false
DEFAULT_BRANCH: master
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VALIDATE_PYTHON_PYLINT: false
VALIDATE_PYTHON_MYPY: false
VALIDATE_JSCPD: false
VALIDATE_PYTHON_BLACK: false
FILTER_REGEX_EXCLUDE: .webfiles/css/bootstrap.*

LINTER_RULES_PATH: /
PYTHON_FLAKE8_CONFIG_FILE: setup.cfg
PYTHON_ISORT_CONFIG_FILE: setup.cfg
YAML_CONFIG_FILE: .yamllint.yaml
---
name: super-linter
#
# Documentation:
# https://help.github.com/en/articles/workflow-syntax-for-github-actions
#

on: pull_request

permissions: read-all

jobs:
build:
name: lint
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v4
with:
# super-linter needs the full git history to get the
# list of files that changed across commits
fetch-depth: 0
- name: Lint Code Base
uses: github/super-linter/slim@v7
env:
VALIDATE_ALL_CODEBASE: false
DEFAULT_BRANCH: master
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VALIDATE_GO_RELEASER: false
VALIDATE_MARKDOWN_PRETTIER: false
VALIDATE_PYTHON_PYLINT: false
VALIDATE_PYTHON_MYPY: false
VALIDATE_JSCPD: false
VALIDATE_PYTHON_BLACK: false
VALIDATE_PYTHON_PYINK: false
FILTER_REGEX_EXCLUDE: .webfiles/css/bootstrap.*

LINTER_RULES_PATH: .
PYTHON_FLAKE8_CONFIG_FILE: .flake8
PYTHON_ISORT_CONFIG_FILE: pyproject.toml
YAML_CONFIG_FILE: .yamllint.yaml
83 changes: 83 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
---
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: check-yaml
- id: check-json
- id: end-of-file-fixer
exclude_types:
- json
- id: trailing-whitespace
- id: no-commit-to-branch
- id: debug-statements
- id: requirements-txt-fixer
- id: check-executables-have-shebangs
- id: detect-private-key
- repo: local
hooks:
- id: update-type-stubs
name: Check for Type Stubs and Update Config
entry: ./update_precommit.py
language: system
files: ^requirements.*\.txt$
stages:
- pre-commit
- repo: https://github.com/PyCQA/flake8
rev: 7.2.0
hooks:
- id: flake8
- repo: https://github.com/pycqa/isort
rev: 6.0.1
hooks:
- id: isort
args:
- --profile
- black
- repo: https://github.com/psf/black
rev: 25.1.0
hooks:
- id: black
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.15.0
hooks:
- id: mypy
entry: mypy OTCamera tests --config-file=pyproject.toml
additional_dependencies:
- art==6.5
- black==25.1.0
- gpiozero==2.0.1
- hatch-requirements-txt==0.4.1
- isort==6.0.1
- mypy==1.15.0
- opencv-python==4.11.0.86
- picamerax==24.3.21
- pre-commit==4.2.0
- pytest-cov==6.1.1
- pytest==8.3.5
- types-PyYAML
- types-RPi.GPIO
- types-beautifulsoup4
- types-flake8
- types-psutil
- types-requests
- types-requests
always_run: true
pass_filenames: false
- repo: https://github.com/adrienverge/yamllint.git
rev: v1.37.1
hooks:
- id: yamllint
args:
- -c=./.yamllint.yaml
- repo: https://github.com/koalaman/shellcheck-precommit
rev: v0.10.0
hooks:
- id: shellcheck
- repo: https://github.com/pecigonzalo/pre-commit-shfmt
rev: v2.2.0
hooks:
- id: shell-fmt-docker
args:
- -i
- "2"
2 changes: 1 addition & 1 deletion .vscode/license.code-snippets
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@
],
"description": "Add GPLv3 license information in source code."
}
}
}
7 changes: 4 additions & 3 deletions OTCamera/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@
every interval (see config.py), captures a new preview image and stops recording
after recording time ends.

Stops everthing by keyboard interrupt (Ctrl+C).
Stops everything by keyboard interrupt (Ctrl+C).

"""

# Copyright (C) 2023 OpenTrafficCam Contributors
# <https://github.com/OpenTrafficCam>
# <team@opentrafficcam.org>
Expand All @@ -28,10 +29,10 @@


# from record import record
from OTCamera.record import record
from OTCamera.record import main as record


def main():
def main() -> None:
"""Starts OTCamera."""
record()

Expand Down
Empty file.
26 changes: 26 additions & 0 deletions OTCamera/abstraction/singleton.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from typing import Any, Optional, Type, TypeVar

T = TypeVar("T", bound="Singleton")


class Singleton(object):
"""Implements the Singleton design pattern.

Classes inheriting from `Singleton` become a singleton class.
Meaning only one instance is created.
Constructing another instance of the concrete class inheriting from `Singleton`
will return the first instance.
"""

__it__: Optional["Singleton"] = None

def __new__(cls: Type[T], *args: Any, **kwargs: Any) -> T:
it = cls.__dict__.get("__it__")
if it is not None:
return it
cls.__it__ = it = object.__new__(cls)
it.init(*args, **kwargs)
return it

def init(self, *args: Any, **kwargs: Any) -> None:
pass
21 changes: 14 additions & 7 deletions OTCamera/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
All the configuration of OTCamera is done here.

"""

# Copyright (C) 2023 OpenTrafficCam Contributors
# <https://github.com/OpenTrafficCam>
# <team@opentrafficcam.org>
Expand All @@ -22,16 +23,17 @@
import socket
import sys
from pathlib import Path
from typing import Literal

try:
from yaml import CSafeLoader as SafeLoader
from yaml import CSafeLoader as SafeLoader # type: ignore
except ImportError:
from yaml import SafeLoader
from yaml import SafeLoader # type: ignore

import yaml


def parse_user_config(config_file: str):
def parse_user_config(config_file: str) -> None:
"""Parses the OTCamera user configuration YAML file.

Args:
Expand Down Expand Up @@ -83,7 +85,6 @@ def parse_user_config(config_file: str):
except KeyError:
_print_key_err_msg("recording.end_hour")
try:
global INTERVAL_LENGTH
setattr(module, "INTERVAL_LENGTH", section["interval_length"])
except KeyError:
_print_key_err_msg("recording.interval_length")
Expand All @@ -101,6 +102,10 @@ def parse_user_config(config_file: str):
except KeyError:
_print_key_err_msg("camera")
else:
try:
setattr(module, "CAMERA_TYPE", section["type"])
except KeyError:
_print_key_err_msg("camera.type")
try:
setattr(module, "FPS", section["fps"])
except KeyError:
Expand Down Expand Up @@ -304,6 +309,8 @@ def read_text_file(text_file: Path) -> str:
"""free space in GB on sd card before old videos get deleted."""

# camera config
CAMERA_TYPE = "legacy"
"""Camera type. `legacy` for the original camera module."""
FPS = 20
"""Frames per Second. 10-20 should be enough."""
RESOLUTION = (1640, 1232)
Expand Down Expand Up @@ -350,13 +357,13 @@ def read_text_file(text_file: Path) -> str:
# video config
VIDEO_DIR = "~/videos/"
"""path to safe videofiles."""
VIDEO_FORMAT = "h264"
VIDEO_FORMAT: Literal["h264"] = "h264"
"""Encoding format."""
RESOLUTION_SAVED_VIDEO_FILE = (800, 600)
"""Resolution of the saved videofile, not the camera itself."""
H264_PROFILE = "high"
H264_PROFILE: Literal["high"] = "high"
"""Profile used in h264 encoder."""
H264_LEVEL = "4"
H264_LEVEL: Literal["4"] = "4"
"""Level used in h264 encoder."""
H264_BITRATE = 600000
"""Bitrate used in h264 encoder."""
Expand Down
Empty file added OTCamera/domain/__init__.py
Empty file.
Loading