Skip to content

Commit

Permalink
Rework PTZ
Browse files Browse the repository at this point in the history
  • Loading branch information
Kane610 committed Nov 11, 2023
1 parent 3dcb9c5 commit 3875ece
Show file tree
Hide file tree
Showing 8 changed files with 629 additions and 480 deletions.
158 changes: 8 additions & 150 deletions axis/vapix/interfaces/param_cgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
ImageParam,
Param,
PropertyParam,
PTZParam,
StreamProfileParam,
)
from ..models.port_cgi import GetPortsRequest, ListInputRequest, ListOutputRequest
Expand Down Expand Up @@ -373,156 +372,15 @@ async def update_ptz(self) -> None:
await self.update(PTZ)

@property
def ptz_params(self) -> PTZParam:
"""Provide property parameters."""
return PTZParam.decode(self[PTZ].raw)

@property
def ptz_camera_default(self) -> int:
"""PTZ default video channel.
When camera parameter is omitted in HTTP requests.
"""
return int(self[PTZ]["CameraDefault"]) # type: ignore

@property
def ptz_number_of_cameras(self) -> int:
"""Amount of video channels."""
return int(self[PTZ]["NbrOfCameras"]) # type: ignore

@property
def ptz_number_of_serial_ports(self) -> int:
"""Amount of serial ports."""
return int(self[PTZ]["NbrOfSerPorts"]) # type: ignore

@property
def ptz_limits(self) -> dict:
"""PTZ.Limit.L# are populated when a driver is installed on a video channel.
Index # is the video channel number, starting on 1.
When it is possible to obtain the current position from the driver,
for example the current pan position, it is possible to apply limit restrictions
to the requested operation. For instance, if an absolute pan to position 150
is requested, but the upper limit is set to 140, the new pan position will be 140.
This is the purpose of all but MinFieldAngle and MaxFieldAngle in this group.
The purpose of those two parameters is to calibrate image centering.
"""
attributes = (
"MaxBrightness",
"MaxFieldAngle",
"MaxFocus",
"MaxIris",
"MaxPan",
"MaxTilt",
"MaxZoom",
"MinBrightness",
"MinFieldAngle",
"MinFocus",
"MinIris",
"MinPan",
"MinTilt",
"MinZoom",
)
return self.process_dynamic_group(
self[PTZ], # type: ignore[arg-type]
"Limit.L",
attributes,
range(1, self.ptz_number_of_cameras + 1),
)

@property
def ptz_support(self) -> dict:
"""PTZ.Support.S# are populated when a driver is installed on a video channel.
A parameter in the group has the value true if the corresponding capability
is supported by the driver. The index # is the video channel number which starts from 1.
An absolute operation means moving to a certain position,
a relative operation means moving relative to the current position.
Arguments referred to apply to PTZ control.
"""
attributes = (
"AbsoluteBrightness",
"AbsoluteFocus",
"AbsoluteIris",
"AbsolutePan",
"AbsoluteTilt",
"AbsoluteZoom",
"ActionNotification",
"AreaZoom",
"AutoFocus",
"AutoIrCutFilter",
"AutoIris",
"Auxiliary",
"BackLight",
"ContinuousBrightness",
"ContinuousFocus",
"ContinuousIris",
"ContinuousPan",
"ContinuousTilt",
"ContinuousZoom",
"DevicePreset",
"DigitalZoom",
"GenericHTTP",
"IrCutFilter",
"JoyStickEmulation",
"LensOffset",
"OSDMenu",
"ProportionalSpeed",
"RelativeBrightness",
"RelativeFocus",
"RelativeIris",
"RelativePan",
"RelativeTilt",
"RelativeZoom",
"ServerPreset",
"SpeedCtl",
)
return self.process_dynamic_group(
self[PTZ], # type: ignore[arg-type]
"Support.S",
attributes,
range(1, self.ptz_number_of_cameras + 1),
)

@property
def ptz_various(self) -> dict:
"""PTZ.Various.V# are populated when a driver is installed on a video channel.
def ptz_data(self) -> bytes:
"""Create a smaller dictionary containing all PTZ information."""
if PTZ not in self:
return b""

The index # is the video channel number which starts from 1.
The group consists of several different types of parameters for the video channel.
To distinguish the parameter types, the group is presented as
three different categories below. The Enabled parameters determine
if a specific feature can be controlled using ptz.cgi (see section PTZ control).
"""
attributes = (
"AutoFocus",
"AutoIris",
"BackLight",
"BackLightEnabled",
"BrightnessEnabled",
"CtlQueueing",
"CtlQueueLimit",
"CtlQueuePollTime",
"FocusEnabled",
"HomePresetSet",
"IrCutFilter",
"IrCutFilterEnabled",
"IrisEnabled",
"MaxProportionalSpeed",
"PanEnabled",
"ProportionalSpeedEnabled",
"PTZCounter",
"ReturnToOverview",
"SpeedCtlEnabled",
"TiltEnabled",
"ZoomEnabled",
)
return self.process_dynamic_group(
self[PTZ], # type: ignore[arg-type]
"Various.V",
attributes,
range(1, self.ptz_number_of_cameras + 1),
)
ptz = ""
for k, v in self[PTZ].raw.items():
ptz += f"root.PTZ.{k}={v}\n"
return ptz.encode()

# Stream profiles

Expand Down
1 change: 0 additions & 1 deletion axis/vapix/interfaces/port_cgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ async def get_ports(self) -> dict[str, Port]:

def process_ports(self) -> dict[str, Port]:
"""Process ports."""
assert self.vapix.params is not None
return GetPortsResponse.decode(self.vapix.params.ports).data

async def action(self, id: str, action: PortAction) -> None:
Expand Down
34 changes: 22 additions & 12 deletions axis/vapix/interfaces/ptz.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,35 @@

from ..models.ptz_cgi import (
DeviceDriverRequest,
GetPtzParamsRequest,
GetPtzResponse,
PtzCommandRequest,
PtzControlRequest,
PtzItem,
PtzMove,
PtzQuery,
PtzRotation,
PtzState,
QueryRequest,
)
from .api_handler import ApiHandler


class PtzControl:
class PtzControl(ApiHandler[PtzItem]):
"""Configure and control the PTZ functionality."""

def __init__(self, vapix) -> None:
"""Initialize PTZ control."""
self.vapix = vapix
async def _api_request(self) -> dict[str, PtzItem]:
"""Get API data method defined by subclass."""
return await self.get_ptz()

async def get_ptz(self) -> dict[str, PtzItem]:
"""Retrieve privilege rights for current user."""
bytes_data = await self.vapix.new_request(GetPtzParamsRequest())
return GetPtzResponse.decode(bytes_data).data

def process_ptz(self) -> dict[str, PtzItem]:
"""Process ports."""
return GetPtzResponse.decode(self.vapix.params.ptz_data).data

async def control(
self,
Expand Down Expand Up @@ -60,11 +73,8 @@ async def control(
ircutfilter: PtzState | None = None,
backlight: bool | None = None,
) -> None:
"""Control the pan, tilt and zoom behavior of a PTZ unit.
move=<string> Absolute:Moves the image 25 % of the image field width in the specified direction.
Relative: Moves the device approx. 50-90 degrees in the specified direction.
"""
return await self.vapix.new_request(
"""Control the pan, tilt and zoom behavior of a PTZ unit."""
await self.vapix.new_request(
PtzControlRequest(
camera,
center,
Expand Down Expand Up @@ -102,14 +112,14 @@ async def control(
)
)

async def query(self, query: PtzQuery) -> str:
async def query(self, query: PtzQuery) -> bytes:
"""Retrieve current status."""
return await self.vapix.new_request(QueryRequest(query))

async def configured_device_driver(self) -> str:
async def configured_device_driver(self) -> bytes:
"""Name of the system-configured device driver."""
return await self.vapix.new_request(DeviceDriverRequest())

async def available_ptz_commands(self) -> str:
async def available_ptz_commands(self) -> bytes:
"""Available PTZ commands."""
return await self.vapix.new_request(PtzCommandRequest())
Loading

0 comments on commit 3875ece

Please sign in to comment.