Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
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
146 changes: 146 additions & 0 deletions CAMERA_IMPLEMENTATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
# Camera System Implementation Summary

## Overview
This implementation adds support for the OpenWebNet Camera/Multimedia system (WHO = 7) to the pyown library. The implementation allows control of video door entry systems and cameras through the OpenWebNet protocol.

## Files Created

### 1. Core Module
- **pyown/items/camera/camera.py** - Main camera implementation
- `Camera` class: Controls camera devices
- `WhatCamera` enum: Defines all camera commands (32 commands total)
- `CameraEvents` enum: Defines callback event types

- **pyown/items/camera/__init__.py** - Module exports

### 2. Documentation
- **docs/api/items/camera.md** - API documentation with examples and usage guide

### 3. Example
- **examples/camera_01/** - Working example demonstrating camera control
- main.py: Example code showing how to use the Camera class
- README.md: Description of the example

### 4. Tests
- **tests/items/test_camera.py** - Unit tests for camera functionality

### 5. Integration
- **pyown/items/__init__.py** - Updated to export camera module

## Implementation Details

### Commands Supported

#### Video Control
- `receive_video()` - Activate camera to receive video (WHAT=0, requires WHERE)
- `free_resources()` - Free audio/video resources (WHAT=9, no WHERE)

#### Zoom Controls
- `zoom_in()` - Zoom in (WHAT=120)
- `zoom_out()` - Zoom out (WHAT=121)
- `increase_x_coordinate()` - Move zoom center right (WHAT=130)
- `decrease_x_coordinate()` - Move zoom center left (WHAT=131)
- `increase_y_coordinate()` - Move zoom center down (WHAT=140)
- `decrease_y_coordinate()` - Move zoom center up (WHAT=141)

#### Image Adjustments
- `increase_luminosity()` / `decrease_luminosity()` - Adjust brightness (WHAT=150/151)
- `increase_contrast()` / `decrease_contrast()` - Adjust contrast (WHAT=160/161)
- `increase_color()` / `decrease_color()` - Adjust color saturation (WHAT=170/171)
- `increase_quality()` / `decrease_quality()` - Adjust image quality (WHAT=180/181)

#### Display Control
- `display_dial(x, y)` - Display specific dial position (WHAT=3XY, where X,Y = 1-4)

### Message Format Handling

The implementation correctly handles two different message formats:

1. **With WHERE parameter** (for receive_video):
```
*7*0*4000## (WHO=7, WHAT=0, WHERE=4000)
```
Uses `NormalMessage` class.

2. **Without WHERE parameter** (for zoom, adjustments, etc.):
```
*7*120## (WHO=7, WHAT=120)
```
Uses `GenericMessage` class via `_send_command_without_where()` helper method.

### Camera Addressing

Cameras are addressed using WHERE values 4000-4099:
- 4000 = Camera 00
- 4001 = Camera 01
- ...
- 4099 = Camera 99

### Video Streaming

Note: The OpenWebNet protocol only handles camera control commands. Actual video streaming is done via HTTP/HTTPS:
```
http://gateway-ip/telecamera.php?CAM_PASSWD=password
```

After activating a camera with `receive_video()`, the JPEG image can be retrieved from this URL.

## Code Style Compliance

The implementation follows the existing code patterns:
- Uses async/await for all commands
- Inherits from `BaseItem`
- Follows the same structure as `Automation` and `Light` items
- Uses proper type hints
- Includes comprehensive docstrings
- Implements event callbacks with `on_status_change()`
- Implements `call_callbacks()` for event dispatching

## Testing

All functionality has been validated:
- ✅ All 32 WHAT commands defined correctly
- ✅ Message formats match OpenWebNet specification
- ✅ Commands with WHERE use NormalMessage
- ✅ Commands without WHERE use GenericMessage
- ✅ Camera class properly inherits from BaseItem
- ✅ WHO is correctly set to VIDEO_DOOR_ENTRY (7)
- ✅ All required methods present and functional
- ✅ Event system properly implemented
- ✅ Unit tests created and passing

## Usage Example

```python
import asyncio
from pyown import Client
from pyown.items import Camera

async def main():
async with Client("192.168.1.35", 20000) as client:
camera = Camera(client, "4000") # Camera 00

# Activate camera
await camera.receive_video()

# Adjust settings
await camera.zoom_in()
await camera.increase_luminosity()
await camera.increase_contrast()

# Display dial
await camera.display_dial(1, 1)

# Free resources
await camera.free_resources()

asyncio.run(main())
```

## Compliance

✅ Follows OpenWebNet WHO=7 specification completely
✅ No HTTP client added (as requested)
✅ Matches existing code style and patterns
✅ Properly documented with examples
✅ Tested and validated
146 changes: 146 additions & 0 deletions docs/api/items/camera.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
---
title: Camera/Multimedia System
summary: Camera and video door entry system control.
---

# Camera Module

The camera module provides support for controlling video door entry systems and cameras
through OpenWebNet (WHO = 7).

## Classes

::: pyown.items.camera.Camera
options:
show_source: false
members:
- receive_video
- free_resources
- zoom_in
- zoom_out
- increase_x_coordinate
- decrease_x_coordinate
- increase_y_coordinate
- decrease_y_coordinate
- increase_luminosity
- decrease_luminosity
- increase_contrast
- decrease_contrast
- increase_color
- decrease_color
- increase_quality
- decrease_quality
- display_dial
- on_status_change

::: pyown.items.camera.WhatCamera
options:
show_source: false

::: pyown.items.camera.CameraEvents
options:
show_source: false

## Camera Addressing

Camera WHERE addresses range from 4000 to 4099:

- `4000`: Camera 00
- `4001`: Camera 01
- `4002`: Camera 02
- ...
- `4099`: Camera 99

## Video Streaming

The OpenWebNet protocol only handles camera control commands. The actual video streaming
is done via HTTP/HTTPS protocol.

After activating a camera with the `receive_video()` command, the JPEG image can be
retrieved from:

```
http://gateway-ip/telecamera.php?CAM_PASSWD=password
```

or

```
https://gateway-ip/telecamera.php?CAM_PASSWD=password
```

If no password is configured, omit the `CAM_PASSWD` parameter (though using a password
is strongly recommended).

## Example Usage

```python
import asyncio
from pyown import Client
from pyown.items.camera import Camera

async def main():
# Connect to the gateway
async with Client("192.168.1.35", 20000) as client:
# Create a camera instance for camera 00 (WHERE = 4000)
camera = Camera(client, "4000")

# Activate the camera to receive video
await camera.receive_video()

# Adjust camera settings
await camera.zoom_in()
await camera.increase_luminosity()
await camera.increase_contrast()

# Display a specific dial
await camera.display_dial(1, 1) # Display DIAL 1-1

# Free resources when done
await camera.free_resources()

asyncio.run(main())
```

## Available Commands

### Video Control

- `receive_video()`: Activate the camera to receive video
- `free_resources()`: Free audio and video resources

### Zoom Controls

- `zoom_in()`: Zoom in the camera view
- `zoom_out()`: Zoom out the camera view
- `increase_x_coordinate()`: Move zoom center right
- `decrease_x_coordinate()`: Move zoom center left
- `increase_y_coordinate()`: Move zoom center down
- `decrease_y_coordinate()`: Move zoom center up

### Image Adjustments

- `increase_luminosity()`: Increase brightness
- `decrease_luminosity()`: Decrease brightness
- `increase_contrast()`: Increase contrast
- `decrease_contrast()`: Decrease contrast
- `increase_color()`: Increase color saturation
- `decrease_color()`: Decrease color saturation
- `increase_quality()`: Increase image quality
- `decrease_quality()`: Decrease image quality

### Display Control

- `display_dial(x, y)`: Display a specific dial position (x, y in range 1-4)

## Event Handling

You can register callbacks to be notified when camera events occur:

```python
from pyown.items.camera import Camera, WhatCamera

@Camera.on_status_change
async def handle_camera_event(camera: Camera, what: WhatCamera):
print(f"Camera {camera.where} event: {what}")
```
12 changes: 12 additions & 0 deletions examples/camera_01/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# camera_01

This example demonstrates how to control a camera/video door entry system using OpenWebNet.

The example shows:
- Activating a camera to receive video
- Adjusting camera settings (zoom, luminosity, contrast)
- Displaying a specific dial
- Freeing video resources when done

Note: The actual video streaming is handled via HTTP/HTTPS and can be accessed at:
`http://gateway-ip/telecamera.php?CAM_PASSWD=password`
73 changes: 73 additions & 0 deletions examples/camera_01/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import asyncio
import logging

from pyown.client import Client
from pyown.items import Camera


async def run(host: str, port: int, password: str):
client = Client(host=host, port=port, password=password)

await client.start()

# Camera 00 is at WHERE address 4000
camera = Camera(client, "4000")

# Activate the camera to receive video
print("Activating camera...")
await camera.receive_video()
print("Camera activated!")
print(f"Video stream available at: http://{host}/telecamera.php")

# Adjust camera settings
print("\nAdjusting camera settings...")
await camera.zoom_in()
await camera.increase_luminosity()
await camera.increase_contrast()

# Display a specific dial
print("\nDisplaying dial 1-1...")
await camera.display_dial(1, 1)

# Wait a bit before freeing resources
await asyncio.sleep(2)

# Free video resources
print("\nFreeing video resources...")
await camera.free_resources()

await client.close()


def main(host: str, port: int, password: str):
# Set the logging level to DEBUG
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
)

# Run the asyncio event loop
asyncio.run(run(host, port, password))


if __name__ == "__main__":
import argparse

parser = argparse.ArgumentParser()
parser.add_argument(
"--host", type=str, help="The host to connect to", default="192.168.1.35"
)
parser.add_argument(
"--port", type=int, help="The port to connect to", default=20000
)
parser.add_argument(
"--password",
type=str,
help="The password to authenticate with",
default="12345",
)

args = parser.parse_args()

main(args.host, args.port, args.password)
1 change: 1 addition & 0 deletions pyown/items/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from .automation import *
from .camera import *
from .energy import *
from .gateway import *
from .lighting import *
7 changes: 7 additions & 0 deletions pyown/items/camera/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from .camera import Camera, WhatCamera, CameraEvents

__all__ = [
"Camera",
"WhatCamera",
"CameraEvents",
]
Loading
Loading