From 4f94ad68fe2561c533e1bd2f1d04aba08af79d6e Mon Sep 17 00:00:00 2001 From: Mateo Date: Sun, 10 Nov 2024 18:10:20 -0300 Subject: [PATCH 1/3] update test version (#228) * update test version * missing refs * change other versions too --- .github/workflows/build-push-image.yml | 2 +- .github/workflows/builds.yml | 6 +++--- .github/workflows/doc-status.yml | 2 +- .github/workflows/docker.yml | 2 +- .github/workflows/docs.yml | 6 +++--- .github/workflows/gh-page.yml | 2 +- .github/workflows/pi.yml | 4 ++-- .github/workflows/release.yml | 30 +++++++++++++------------- .github/workflows/style.yml | 22 +++++++++---------- .github/workflows/tests.yml | 22 +++++++++---------- 10 files changed, 49 insertions(+), 49 deletions(-) diff --git a/.github/workflows/build-push-image.yml b/.github/workflows/build-push-image.yml index 19496ad2..97980f41 100644 --- a/.github/workflows/build-push-image.yml +++ b/.github/workflows/build-push-image.yml @@ -12,7 +12,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Log in to Docker Hub run: echo "${{ secrets.DOCKERHUB_PW }}" | docker login -u "${{ secrets.DOCKERHUB_LOGIN }}" --password-stdin diff --git a/.github/workflows/builds.yml b/.github/workflows/builds.yml index f7d92334..caa46b09 100644 --- a/.github/workflows/builds.yml +++ b/.github/workflows/builds.yml @@ -15,14 +15,14 @@ jobs: os: [ubuntu-latest] python: [3.8] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Python - uses: actions/setup-python@v1 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} architecture: x64 - name: Cache python modules - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: ~/.cache/pip key: ${{ runner.os }}-python-${{ matrix.python }}-${{ hashFiles('pyproject.toml') }}-build diff --git a/.github/workflows/doc-status.yml b/.github/workflows/doc-status.yml index 450a5147..b5d32045 100644 --- a/.github/workflows/doc-status.yml +++ b/.github/workflows/doc-status.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Set up Python - uses: actions/setup-python@v1 + uses: actions/setup-python@v4 with: python-version: 3.8 architecture: x64 diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index c68bb7ed..b800f24b 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -10,7 +10,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Build docker image run: make build-app - name: Run docker container diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 995e331a..79ea3ee5 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -11,16 +11,16 @@ jobs: os: [ubuntu-latest] python: [3.8] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: persist-credentials: false - name: Set up Python - uses: actions/setup-python@v1 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} architecture: x64 - name: Cache python modules - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: ~/.cache/pip key: ${{ runner.os }}-python-${{ matrix.python }}-${{ hashFiles('pyproject.toml') }}-docs diff --git a/.github/workflows/gh-page.yml b/.github/workflows/gh-page.yml index eb3a6662..2c62ff6f 100644 --- a/.github/workflows/gh-page.yml +++ b/.github/workflows/gh-page.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Set up Python 3.8 - uses: actions/setup-python@v1 + uses: actions/setup-python@v4 with: python-version: 3.8 architecture: x64 diff --git a/.github/workflows/pi.yml b/.github/workflows/pi.yml index 0af93a8b..fa9bce0d 100644 --- a/.github/workflows/pi.yml +++ b/.github/workflows/pi.yml @@ -14,7 +14,7 @@ # matrix: # os: [self-hosted] # steps: -# - uses: actions/checkout@v2 +# - uses: actions/checkout@v4 # - name: Install package # run: | # python -m pip install --upgrade pip @@ -28,7 +28,7 @@ # os: [self-hosted] # needs: build # steps: -# - uses: actions/checkout@v2 +# - uses: actions/checkout@v4 # - name: Install dependencies # run: pip install -e ".[test]" --upgrade diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index cf94c1ac..3fa1bd07 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -6,17 +6,17 @@ on: jobs: pypi-publish: - if: "!github.event.release.prerelease" + if: !github.event.release.prerelease runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Python - uses: actions/setup-python@v1 + uses: actions/setup-python@v4 with: python-version: 3.8 architecture: x64 - name: Cache python modules - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: ~/.cache/pip key: ${{ runner.os }}-python-${{ matrix.python }}-${{ hashFiles('pyproject.toml') }}-build @@ -43,9 +43,9 @@ jobs: runs-on: ubuntu-latest needs: pypi-publish steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Python - uses: actions/setup-python@v1 + uses: actions/setup-python@v4 with: python-version: 3.8 architecture: x64 @@ -59,9 +59,9 @@ jobs: if: "!github.event.release.prerelease" runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Miniconda setup - uses: conda-incubator/setup-miniconda@v2 + uses: conda-incubator/setup-miniconda@v4 with: auto-update-conda: true python-version: 3.8 @@ -92,9 +92,9 @@ jobs: os: [ubuntu-latest, macos-latest, windows-latest] python: [3.8] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Miniconda setup - uses: conda-incubator/setup-miniconda@v1 + uses: conda-incubator/setup-miniconda@v4 with: auto-update-conda: true python-version: ${{ matrix.python }} @@ -110,19 +110,19 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out the repo - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Set up QEMU - uses: docker/setup-qemu-action@v1 + uses: docker/setup-qemu-action@v4 - name: Set up Docker Buildx id: buildx - uses: docker/setup-buildx-action@v1 + uses: docker/setup-buildx-action@v4 - name: Login to Docker Hub - uses: docker/login-action@v1 + uses: docker/login-action@v4 with: username: ${{ secrets.DOCKERHUB_LOGIN }} password: ${{ secrets.DOCKERHUB_PW }} - name: Push to Docker Hub - uses: docker/build-push-action@v1 + uses: docker/build-push-action@v4 with: context: . platforms: linux/amd64,linux/arm64 diff --git a/.github/workflows/style.yml b/.github/workflows/style.yml index 8521c188..eca5de62 100644 --- a/.github/workflows/style.yml +++ b/.github/workflows/style.yml @@ -14,9 +14,9 @@ jobs: os: [ubuntu-latest] python: [3.8] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Python - uses: actions/setup-python@v1 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} architecture: x64 @@ -33,9 +33,9 @@ jobs: os: [ubuntu-latest] python: [3.8] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Python - uses: actions/setup-python@v1 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} architecture: x64 @@ -53,14 +53,14 @@ jobs: os: [ubuntu-latest] python: [3.8] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Python - uses: actions/setup-python@v1 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} architecture: x64 - name: Cache python modules - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: ~/.cache/pip key: ${{ runner.os }}-python-${{ matrix.python }}-${{ hashFiles('pyproject.toml') }} @@ -81,9 +81,9 @@ jobs: os: [ubuntu-latest] python: [3.8] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} architecture: x64 @@ -100,9 +100,9 @@ jobs: os: [ubuntu-latest] python: [3.8] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} architecture: x64 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 4d5af5ae..03d810c8 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -14,16 +14,16 @@ jobs: os: [ubuntu-latest] python: [3.8] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: persist-credentials: false - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} architecture: x64 - name: Cache python modules - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: ~/.cache/pip key: ${{ runner.os }}-python-${{ matrix.python }}-${{ hashFiles('pyproject.toml') }}-test @@ -41,7 +41,7 @@ jobs: run: | coverage run -m pytest tests/ coverage xml - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v4 with: name: coverage-main path: ./coverage.xml @@ -50,13 +50,13 @@ jobs: runs-on: ubuntu-latest needs: pytest steps: - - uses: actions/checkout@v2 - - uses: actions/download-artifact@v2 + - uses: actions/checkout@v4 + - uses: actions/download-artifact@v4 with: name: coverage-main path: ./coverage.xml - name: Upload coverage to Codecov - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} # Use the Codecov token from secrets flags: unittests @@ -69,16 +69,16 @@ jobs: os: [ubuntu-latest] python: [3.8] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: persist-credentials: false - name: Set up Python - uses: actions/setup-python@v1 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} architecture: x64 - name: Cache python modules - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: ~/.cache/pip key: ${{ runner.os }}-python-${{ matrix.python }}-${{ hashFiles('pyproject.toml') }}-docs @@ -97,7 +97,7 @@ jobs: matrix: os: [ubuntu-latest] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: persist-credentials: false - name: Check the headers From f10f7b286a1948b51295303d7d2d75c291f47d42 Mon Sep 17 00:00:00 2001 From: Mateo Date: Sun, 10 Nov 2024 18:23:07 -0300 Subject: [PATCH 2/3] Reolink commands (#226) * add missing commands * use default pwd and add docstring * missing load * save image * remove cam type * header * put back type with default value * prevent image none --- pyroengine/sensors.py | 31 +++--------- src/control_reolink_cam.py | 101 ++++++++++++++++++++++++++++++++++--- 2 files changed, 102 insertions(+), 30 deletions(-) diff --git a/pyroengine/sensors.py b/pyroengine/sensors.py index 62131006..af9bbdac 100644 --- a/pyroengine/sensors.py +++ b/pyroengine/sensors.py @@ -27,7 +27,7 @@ class ReolinkCamera: ip_address (str): IP address of the Reolink camera. username (str): Username for accessing the camera. password (str): Password for accessing the camera. - cam_type (str): Type of the camera, e.g., 'static' or 'ptz' (pan-tilt-zoom). + cam_type (str): Type of the camera, e.g., 'static' or 'ptz' (pan-tilt-zoom), defaults to 'ptz'. cam_poses (Optional[List[int]]): List of preset positions for PTZ cameras. protocol (str): Protocol used for communication, defaults to 'https'. @@ -44,7 +44,7 @@ def __init__( ip_address: str, username: str, password: str, - cam_type: str, + cam_type: str = "ptz", cam_poses: Optional[List[int]] = None, protocol: str = "https", ): @@ -121,19 +121,23 @@ def move_camera(self, operation: str, speed: int = 20, idx: int = 0): response = requests.post(url, json=data, verify=False) # nosec: B501 self._handle_response(response, "PTZ operation successful.") - def move_in_seconds(self, s: int, operation: str = "Right", speed: int = 20): + def move_in_seconds(self, s: float, operation: str = "Right", speed: int = 20, save_path: str = "im.jpg"): """ Moves the camera in a specified direction for a specified number of seconds. Args: - s (int): Duration in seconds to move the camera. + s (float): Duration in seconds to move the camera. operation (str): Direction to move the camera. speed (int): Speed of the movement. + save_path (str): After movement capture and save image at save_path """ self.move_camera(operation, speed) time.sleep(s) self.move_camera("Stop") time.sleep(1) + im = self.capture() + if im is not None: + im.save(save_path) def get_ptz_preset(self): """ @@ -183,25 +187,6 @@ def set_ptz_preset(self, idx: Optional[int] = None): # Utilizing the shared response handling method self._handle_response(response, f"Preset {name} set successfully.") - def delete_ptz_preset(self, idx: int): - """ - Deletes a PTZ preset position by setting its enable value to 0. - - Args: - idx (int): The preset ID to delete. - """ - url = self._build_url("SetPtzPreset") - data = [ - { - "cmd": "SetPtzPreset", - "action": 0, # The action code for setting data - "param": {"PtzPreset": {"channel": 0, "enable": 0, "id": idx}}, - } - ] - response = requests.post(url, json=data, verify=False) # nosec: B501 - # Utilizing the shared response handling method - self._handle_response(response, f"Preset {idx} deleted successfully.") - def reboot_camera(self): url = self._build_url("Reboot") data = [{"cmd": "Reboot"}] diff --git a/src/control_reolink_cam.py b/src/control_reolink_cam.py index c928d4d6..44e5fcb6 100644 --- a/src/control_reolink_cam.py +++ b/src/control_reolink_cam.py @@ -1,26 +1,92 @@ -# Copyright (C) 2023-2024, Pyronear. +# Copyright (C) 2022-2024, Pyronear. # This program is licensed under the Apache License 2.0. # See LICENSE or go to for full license details. - import argparse +import os + +from dotenv import load_dotenv from pyroengine.sensors import ReolinkCamera def main(): + """ + Control Reolink Camera for various operations. + + This script allows you to interact with a Reolink camera to perform various actions like capturing images, + moving the camera, handling PTZ presets, and more. + + Available actions: + - `capture`: Captures an image from the camera. + - `move_camera`: Moves the camera in a specified direction or to a preset position. + - `move_in_seconds`: Moves the camera in a specified direction for a certain duration. + - `get_ptz_preset`: Retrieves the list of PTZ preset positions. + - `set_ptz_preset`: Sets a PTZ preset position. + - `delete_ptz_preset`: Deletes a PTZ preset position. + - `reboot_camera`: Reboots the camera. + - `get_auto_focus`: Retrieves the current auto-focus settings. + - `set_auto_focus`: Enables or disables the auto-focus. + - `start_zoom_focus`: Starts zooming the camera to a specific focus position. + + Examples: + - Capture an image: + python src/control_reolink_cam.py capture --ip 169.254.40.1 --type ptz + + - Move the camera to a preset position: + python src/control_reolink_cam.py move_camera --ip 169.254.40.1 --pos_id 0 --operation ToPos + + - Move the camera to the right for 3 seconds: + python src/control_reolink_cam.py move_in_seconds --ip 169.254.40.1 --operation Right --duration 3 + + - Get the list of PTZ presets: + python src/control_reolink_cam.py get_ptz_preset --ip 169.254.40.1 --type ptz + + - Set a PTZ preset at position 1: + python src/control_reolink_cam.py set_ptz_preset --ip 169.254.40.1 --pos_id 1 + + - Delete a PTZ preset at position 1: + python src/control_reolink_cam.py delete_ptz_preset --ip 169.254.40.1 --pos_id 1 + + - Reboot the camera: + python src/control_reolink_cam.py reboot_camera --ip 169.254.40.1 --type ptz + + - Get the auto-focus settings: + python src/control_reolink_cam.py get_auto_focus --ip 169.254.40.1 --type ptz + + - Disable auto-focus: + python src/control_reolink_cam.py set_auto_focus --ip 169.254.40.1 --disable_autofocus + + - Start zooming to focus position 5: + python src/control_reolink_cam.py start_zoom_focus --ip 169.254.40.1 --zoom_position 5 + """ + # Load environment variables + load_dotenv() + cam_user = os.getenv("CAM_USER") + cam_pwd = os.getenv("CAM_PWD") + # Set up argument parsing parser = argparse.ArgumentParser(description="Control Reolink Camera for various operations.") parser.add_argument( "action", - choices=["capture", "move_camera", "move_in_seconds", "get_ptz_preset", "set_ptz_preset"], + choices=[ + "capture", + "move_camera", + "move_in_seconds", + "get_ptz_preset", + "set_ptz_preset", + "delete_ptz_preset", + "reboot_camera", + "get_auto_focus", + "set_auto_focus", + "start_zoom_focus", + ], help="Action to perform on the camera", ) parser.add_argument("--ip", required=True, help="IP address of the Reolink camera") - parser.add_argument("--username", required=True, help="Username for camera access") - parser.add_argument("--password", required=True, help="Password for camera access") - parser.add_argument("--type", required=True, choices=["static", "ptz"], help="Type of the camera") + parser.add_argument("--username", help="Username for camera access", default=cam_user) + parser.add_argument("--password", help="Password for camera access", default=cam_pwd) parser.add_argument("--protocol", help="Protocol (http or https)", default="http") parser.add_argument( "--pos_id", type=int, help="Position ID for moving the camera or capturing at a specific position", default=None @@ -28,13 +94,15 @@ def main(): parser.add_argument("--operation", help="Operation type for moving the camera (e.g., 'Left', 'Right')") parser.add_argument("--speed", type=int, help="Speed of the PTZ movement", default=1) parser.add_argument("--duration", type=int, help="Duration in seconds for moving the camera", default=1) + parser.add_argument("--disable_autofocus", action="store_true", help="Disable autofocus if set") + parser.add_argument("--zoom_position", type=int, help="Zoom position for start_zoom_focus", default=None) args = parser.parse_args() print(args) # Create an instance of ReolinkCamera camera_controller = ReolinkCamera( - ip_address=args.ip, username=args.username, password=args.password, cam_type=args.type, protocol=args.protocol + ip_address=args.ip, username=args.username, password=args.password, protocol=args.protocol ) # Handling different actions @@ -62,6 +130,25 @@ def main(): camera_controller.set_ptz_preset(idx=args.pos_id) else: print("Position ID must be provided for setting a PTZ preset.") + elif args.action == "delete_ptz_preset": + if args.pos_id is not None: + camera_controller.delete_ptz_preset(idx=args.pos_id) + else: + print("Position ID must be provided for deleting a PTZ preset.") + elif args.action == "reboot_camera": + camera_controller.reboot_camera() + print("Camera reboot initiated.") + elif args.action == "get_auto_focus": + autofocus_settings = camera_controller.get_auto_focus() + print("AutoFocus Settings:", autofocus_settings) + elif args.action == "set_auto_focus": + camera_controller.set_auto_focus(disable=args.disable_autofocus) + print(f"AutoFocus {'disabled' if args.disable_autofocus else 'enabled'}.") + elif args.action == "start_zoom_focus": + if args.zoom_position is not None: + camera_controller.start_zoom_focus(position=args.zoom_position) + else: + print("Zoom position must be provided for starting zoom focus.") if __name__ == "__main__": From cb2f13bb6aba2b55aa5b091f987ee2bc67ffe9f1 Mon Sep 17 00:00:00 2001 From: RonanMorgan <49660557+RonanMorgan@users.noreply.github.com> Date: Sun, 10 Nov 2024 22:23:28 +0100 Subject: [PATCH 3/3] fix (#220) Co-authored-by: Ronan --- src/run.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/run.py b/src/run.py index 393ffee5..e642b321 100644 --- a/src/run.py +++ b/src/run.py @@ -27,7 +27,6 @@ def main(args): # .env loading load_dotenv(".env") API_URL = os.environ.get("API_URL") - API_URL = "https://api.pyronear.org" LAT = float(os.environ.get("LAT")) LON = float(os.environ.get("LON")) assert isinstance(API_URL, str) and isinstance(LAT, float) and isinstance(LON, float)