Device Control API

Device Control Client

class neoruntime_ipc_sdk.device.IrCutMode(value)[source]

Bases: Enum

AUTO = 0
DAY = 1
NIGHT = 2
class neoruntime_ipc_sdk.device.DeviceStatus(soc_temp_c: 'float', mcu_temp_c: 'float', light_sensor: 'int', ptz_pan_pos: 'int', ptz_tilt_pos: 'int', zoom_pos: 'int', focus_pos: 'int', autofocus_enabled: 'bool', ircut_mode: 'IrCutMode', white_light_level: 'int', ir_led_on: 'bool', mcu_version: 'str', mcu_uptime_ms: 'int')[source]

Bases: object

soc_temp_c: float
mcu_temp_c: float
light_sensor: int
ptz_pan_pos: int
ptz_tilt_pos: int
zoom_pos: int
focus_pos: int
autofocus_enabled: bool
ircut_mode: IrCutMode
white_light_level: int
ir_led_on: bool
mcu_version: str
mcu_uptime_ms: int
__init__(soc_temp_c, mcu_temp_c, light_sensor, ptz_pan_pos, ptz_tilt_pos, zoom_pos, focus_pos, autofocus_enabled, ircut_mode, white_light_level, ir_led_on, mcu_version, mcu_uptime_ms)
class neoruntime_ipc_sdk.device.DeviceEvent(type: 'EventType', timestamp_ns: 'int', gpio_pin: 'int' = 0, gpio_value: 'bool' = False, light_sensor_value: 'int' = 0, temperature: 'float' = 0.0)[source]

Bases: object

class EventType(value)[source]

Bases: Enum

GPIO_CHANGE = 0
LIGHT_SENSOR_CHANGE = 1
TEMPERATURE_ALERT = 2
PTZ_MOVE_COMPLETE = 3
FOCUS_COMPLETE = 4
type: EventType
timestamp_ns: int
gpio_pin: int = 0
gpio_value: bool = False
light_sensor_value: int = 0
temperature: float = 0.0
__init__(type, timestamp_ns, gpio_pin=0, gpio_value=False, light_sensor_value=0, temperature=0.0)
class neoruntime_ipc_sdk.device.AfJob(accepted, job_id, message)[source]

Bases: object

Result of starting an autofocus job (native AF RPCs).

accepted: bool
job_id: int
message: str
__init__(accepted, job_id, message)
class neoruntime_ipc_sdk.device.AfStatus(job_id, operation, state, progress, busy, anchor_valid, requested_ratio, effective_ratio, zoom_pos, focus_pos, best_focus, metric, confidence, reproducibility, estimated_distance_m, elapsed_ms, error_code, message)[source]

Bases: object

Full snapshot of the autofocus engine state.

job_id: int
operation: str
state: str
progress: float
busy: bool
anchor_valid: bool
requested_ratio: float
effective_ratio: float
zoom_pos: int
focus_pos: int
best_focus: int
metric: float
confidence: float
reproducibility: float
estimated_distance_m: float
elapsed_ms: int
error_code: int
message: str
__init__(job_id, operation, state, progress, busy, anchor_valid, requested_ratio, effective_ratio, zoom_pos, focus_pos, best_focus, metric, confidence, reproducibility, estimated_distance_m, elapsed_ms, error_code, message)
class neoruntime_ipc_sdk.device.AfMeasurement(focus_energy, mean_luma, frame_id)[source]

Bases: object

Per-window focus telemetry from the last AF measurement.

focus_energy: list
mean_luma: list
frame_id: int
__init__(focus_energy, mean_luma, frame_id)
class neoruntime_ipc_sdk.device.DeviceClient(endpoint=None)[source]

Bases: GrpcClient

Device Control Client

Usage:

dev = DeviceClient()

dev.set_white_light(80) dev.set_ir_led(True) dev.set_ircut(IrCutMode.NIGHT)

dev.pan_left(speed=50) dev.call_preset(3)

dev.zoom_in() dev.focus_auto()

set_white_light(level)[source]
set_ir_led(on)[source]
set_ircut(mode)[source]
pan_left(speed=50)[source]
pan_right(speed=50)[source]
pan_stop()[source]
tilt_up(speed=50)[source]
tilt_down(speed=50)[source]
tilt_stop()[source]
ptz_stop()[source]
save_preset(preset_id)[source]
call_preset(preset_id)[source]
zoom_in(speed=50)[source]
zoom_out(speed=50)[source]
zoom_stop()[source]
zoom(speed)[source]
set_zoom_level(level)[source]
focus_in(speed=50)[source]
focus_out(speed=50)[source]
focus_stop()[source]
focus(speed)[source]
focus_auto(enable=True)[source]
set_focus_level(level)[source]
get_lens_status()[source]
set_lens_limits(zoom_limit=None, focus_limit=None)[source]

Set lens axis position limits.

Parameters:
  • zoom_limit (dict[str, int] | None) – Dict with min_pos and max_pos keys, or None to skip.

  • focus_limit (dict[str, int] | None) – Dict with min_pos and max_pos keys, or None to skip.

Example:

dev.set_lens_limits(zoom_limit={"min_pos": 0, "max_pos": 1000})
dev.set_lens_limits(
    zoom_limit={"min_pos": 0, "max_pos": 1000},
    focus_limit={"min_pos": 0, "max_pos": 800},
)
oneshot_autofocus(timeout=20.0)[source]

Perform a single autofocus cycle: enable → wait for convergence → disable.

This is a composite operation that: 1. Enables continuous autofocus 2. Polls lens status until focus motor settles (or timeout) 3. Disables continuous autofocus

Parameters:

timeout (float) – Maximum seconds to wait for focus convergence (default: 20.0)

Raises:
  • RuntimeError – If autofocus fails to converge within timeout

  • TimeoutError – If focus motor does not settle within timeout

start_oneshot_af()[source]

Start a one-shot autofocus job and return its handle immediately.

Unlike oneshot_autofocus() (a blocking composite over the legacy SetAutofocus RPC), this returns at once; pair it with get_autofocus_status() to poll for convergence.

start_zoom_follow(ratio)[source]

Start continuous autofocus locked to a zoom ratio.

Parameters:

ratio (float) – Zoom ratio the focus engine should track (e.g. 1.5).

get_autofocus_status()[source]

Snapshot of the autofocus engine (job, progress, lens positions).

cancel_autofocus(job_id=0)[source]

Cancel an autofocus job.

Parameters:

job_id (int) – Job to cancel; 0 (default) cancels the active job.

set_af_windows(enabled, windows, stream_id='main')[source]

Set autofocus measurement windows for a stream.

Parameters:
  • enabled (bool) – Whether AF windows are active.

  • windows – 1-3 (x, y, w, h) pixel rectangles. An AF window restricts the image region the focus metric is computed on.

  • stream_id (str) – Stream the windows apply to (default “main”).

get_af_measurement()[source]

Latest per-window focus telemetry (focus energy / luma).

set_wiegand_out(channel, enable)[source]

Enable or disable a Wiegand output channel.

Parameters:
  • channel (int) – Wiegand channel number (typically 0 or 1)

  • enable (bool) – True to enable, False to disable

get_wiegand_out(channel)[source]

Get the enabled state of a Wiegand output channel.

Parameters:

channel (int) – Wiegand channel number (typically 0 or 1)

Returns:

True if the channel is enabled

Return type:

bool

rs485_init(baudrate, config='')[source]

Initialize RS-485 serial interface.

Parameters:
  • baudrate (int) – Baud rate (e.g. 9600, 115200)

  • config (str) – Optional configuration string

rs485_deinit()[source]

Deinitialize RS-485 serial interface.

rs485_tx(data)[source]

Transmit data over RS-485.

Parameters:

data (bytes) – Bytes to transmit

lens_reset_zero(zoom=True, focus=True)[source]
control_iris(open)[source]
set_iris_target(target)[source]
lens_init()[source]
lens_goto_ratio_distance(zoom_ratio, focus_distance_m)[source]
gpio_set(pin, value)[source]
gpio_get(pin)[source]
get_device_status()[source]
subscribe_events()[source]

DeviceClient

class neoruntime_ipc_sdk.DeviceClient(endpoint=None)[source]

Bases: GrpcClient

Device Control Client

Usage:

dev = DeviceClient()

dev.set_white_light(80) dev.set_ir_led(True) dev.set_ircut(IrCutMode.NIGHT)

dev.pan_left(speed=50) dev.call_preset(3)

dev.zoom_in() dev.focus_auto()

set_white_light(level)[source]
set_ir_led(on)[source]
set_ircut(mode)[source]
pan_left(speed=50)[source]
pan_right(speed=50)[source]
pan_stop()[source]
tilt_up(speed=50)[source]
tilt_down(speed=50)[source]
tilt_stop()[source]
ptz_stop()[source]
save_preset(preset_id)[source]
call_preset(preset_id)[source]
zoom_in(speed=50)[source]
zoom_out(speed=50)[source]
zoom_stop()[source]
zoom(speed)[source]
set_zoom_level(level)[source]
focus_in(speed=50)[source]
focus_out(speed=50)[source]
focus_stop()[source]
focus(speed)[source]
focus_auto(enable=True)[source]
set_focus_level(level)[source]
get_lens_status()[source]
set_lens_limits(zoom_limit=None, focus_limit=None)[source]

Set lens axis position limits.

Parameters:
  • zoom_limit (dict[str, int] | None) – Dict with min_pos and max_pos keys, or None to skip.

  • focus_limit (dict[str, int] | None) – Dict with min_pos and max_pos keys, or None to skip.

Example:

dev.set_lens_limits(zoom_limit={"min_pos": 0, "max_pos": 1000})
dev.set_lens_limits(
    zoom_limit={"min_pos": 0, "max_pos": 1000},
    focus_limit={"min_pos": 0, "max_pos": 800},
)
oneshot_autofocus(timeout=20.0)[source]

Perform a single autofocus cycle: enable → wait for convergence → disable.

This is a composite operation that: 1. Enables continuous autofocus 2. Polls lens status until focus motor settles (or timeout) 3. Disables continuous autofocus

Parameters:

timeout (float) – Maximum seconds to wait for focus convergence (default: 20.0)

Raises:
  • RuntimeError – If autofocus fails to converge within timeout

  • TimeoutError – If focus motor does not settle within timeout

start_oneshot_af()[source]

Start a one-shot autofocus job and return its handle immediately.

Unlike oneshot_autofocus() (a blocking composite over the legacy SetAutofocus RPC), this returns at once; pair it with get_autofocus_status() to poll for convergence.

start_zoom_follow(ratio)[source]

Start continuous autofocus locked to a zoom ratio.

Parameters:

ratio (float) – Zoom ratio the focus engine should track (e.g. 1.5).

get_autofocus_status()[source]

Snapshot of the autofocus engine (job, progress, lens positions).

cancel_autofocus(job_id=0)[source]

Cancel an autofocus job.

Parameters:

job_id (int) – Job to cancel; 0 (default) cancels the active job.

set_af_windows(enabled, windows, stream_id='main')[source]

Set autofocus measurement windows for a stream.

Parameters:
  • enabled (bool) – Whether AF windows are active.

  • windows – 1-3 (x, y, w, h) pixel rectangles. An AF window restricts the image region the focus metric is computed on.

  • stream_id (str) – Stream the windows apply to (default “main”).

get_af_measurement()[source]

Latest per-window focus telemetry (focus energy / luma).

set_wiegand_out(channel, enable)[source]

Enable or disable a Wiegand output channel.

Parameters:
  • channel (int) – Wiegand channel number (typically 0 or 1)

  • enable (bool) – True to enable, False to disable

get_wiegand_out(channel)[source]

Get the enabled state of a Wiegand output channel.

Parameters:

channel (int) – Wiegand channel number (typically 0 or 1)

Returns:

True if the channel is enabled

Return type:

bool

rs485_init(baudrate, config='')[source]

Initialize RS-485 serial interface.

Parameters:
  • baudrate (int) – Baud rate (e.g. 9600, 115200)

  • config (str) – Optional configuration string

rs485_deinit()[source]

Deinitialize RS-485 serial interface.

rs485_tx(data)[source]

Transmit data over RS-485.

Parameters:

data (bytes) – Bytes to transmit

lens_reset_zero(zoom=True, focus=True)[source]
control_iris(open)[source]
set_iris_target(target)[source]
lens_init()[source]
lens_goto_ratio_distance(zoom_ratio, focus_distance_m)[source]
gpio_set(pin, value)[source]
gpio_get(pin)[source]
get_device_status()[source]
subscribe_events()[source]

Data Types

DeviceStatus

class neoruntime_ipc_sdk.DeviceStatus(soc_temp_c: 'float', mcu_temp_c: 'float', light_sensor: 'int', ptz_pan_pos: 'int', ptz_tilt_pos: 'int', zoom_pos: 'int', focus_pos: 'int', autofocus_enabled: 'bool', ircut_mode: 'IrCutMode', white_light_level: 'int', ir_led_on: 'bool', mcu_version: 'str', mcu_uptime_ms: 'int')[source]
soc_temp_c: float
mcu_temp_c: float
light_sensor: int
ptz_pan_pos: int
ptz_tilt_pos: int
zoom_pos: int
focus_pos: int
autofocus_enabled: bool
ircut_mode: IrCutMode
white_light_level: int
ir_led_on: bool
mcu_version: str
mcu_uptime_ms: int
__init__(soc_temp_c, mcu_temp_c, light_sensor, ptz_pan_pos, ptz_tilt_pos, zoom_pos, focus_pos, autofocus_enabled, ircut_mode, white_light_level, ir_led_on, mcu_version, mcu_uptime_ms)

DeviceEvent

class neoruntime_ipc_sdk.DeviceEvent(type: 'EventType', timestamp_ns: 'int', gpio_pin: 'int' = 0, gpio_value: 'bool' = False, light_sensor_value: 'int' = 0, temperature: 'float' = 0.0)[source]
class EventType(value)[source]
GPIO_CHANGE = 0
LIGHT_SENSOR_CHANGE = 1
TEMPERATURE_ALERT = 2
PTZ_MOVE_COMPLETE = 3
FOCUS_COMPLETE = 4
type: EventType
timestamp_ns: int
gpio_pin: int = 0
gpio_value: bool = False
light_sensor_value: int = 0
temperature: float = 0.0
__init__(type, timestamp_ns, gpio_pin=0, gpio_value=False, light_sensor_value=0, temperature=0.0)

IrCutMode

class neoruntime_ipc_sdk.IrCutMode(value)[source]
AUTO = 0
DAY = 1
NIGHT = 2

Usage Examples

Light Control

from neoruntime_ipc_sdk import DeviceClient, IrCutMode

dev = DeviceClient()

# White light control
dev.set_white_light(0)      # Off
dev.set_white_light(50)     # 50% brightness
dev.set_white_light(100)    # 100% brightness

# IR LED control
dev.set_ir_led(True)        # On
dev.set_ir_led(False)       # Off

# IR cut filter control
dev.set_ircut(IrCutMode.DAY)    # Day mode (filter on)
dev.set_ircut(IrCutMode.NIGHT)  # Night mode (filter off)
dev.set_ircut(IrCutMode.AUTO)   # Auto mode

PTZ Control

# Pan movement
dev.pan_left(speed=50)   # Move left
dev.pan_right(speed=50)  # Move right
dev.pan_stop()           # Stop horizontal movement

# Tilt movement
dev.tilt_up(speed=50)    # Move up
dev.tilt_down(speed=50)  # Move down
dev.tilt_stop()          # Stop vertical movement

# Stop all PTZ movement
dev.ptz_stop()

Preset Control

# Save preset
dev.save_preset(1)  # Save current position to preset 1

# Call preset
dev.call_preset(1)  # Move to preset 1

Lens Control

# Lens initialization
dev.lens_init()              # Initialize lens module

# Zoom
dev.zoom_in(speed=50)        # Zoom in
dev.zoom_out(speed=50)       # Zoom out
dev.zoom_stop()              # Stop zoom

# Set zoom level (0.0 ~ 1.0)
dev.set_zoom_level(0.5)      # 50% zoom position

# Focus
dev.focus_in(speed=50)       # Far focus
dev.focus_out(speed=50)      # Near focus
dev.focus_stop()             # Stop focus

# Set focus level (0.0 ~ 1.0)
dev.set_focus_level(0.5)     # 50% focus position

# Auto focus
dev.focus_auto(enable=True)   # Enable auto focus
dev.focus_auto(enable=False)  # Disable auto focus

# One-shot auto focus (composite: enable → wait for convergence → disable)
dev.oneshot_autofocus(timeout=20.0)

# Zoom + focus linked move (by optical zoom ratio and focus distance)
dev.lens_goto_ratio_distance(zoom_ratio=2.0, focus_distance_m=3.0)

# Iris control
dev.control_iris(open=True)   # Open iris
dev.set_iris_target(128)      # Set iris target value

# Lens reset zero
dev.lens_reset_zero(zoom=True, focus=True)   # Reset both axes

# Set lens limits
dev.set_lens_limits(zoom_limit={"min_pos": 0, "max_pos": 1000})
dev.set_lens_limits(
    zoom_limit={"min_pos": 0, "max_pos": 1000},
    focus_limit={"min_pos": 0, "max_pos": 800},
)

# Get lens status
status = dev.get_lens_status()
print(f"Zoom pos: {status['zoom_pos']}")
print(f"Focus pos: {status['focus_pos']}")
print(f"Zoom state: {status['zoom_state']}")  # 0=not ready, 1=idle, 2=moving
print(f"Autofocus: {status['autofocus_enabled']}")

GPIO Control

# Read GPIO
value = dev.gpio_get(12)
print(f"GPIO 12 state: {'high' if value else 'low'}")

# Write GPIO
dev.gpio_set(21, True)   # Set high
dev.gpio_set(21, False)  # Set low

# Control relay
dev.gpio_set(22, True)   # Turn on relay

Wiegand Control

# Set Wiegand output
dev.set_wiegand_out(channel=0, enable=True)   # Enable Wiegand channel 0
dev.set_wiegand_out(channel=1, enable=False)  # Disable Wiegand channel 1

# Query Wiegand output state
enabled = dev.get_wiegand_out(channel=0)
print(f"Wiegand channel 0: {'enabled' if enabled else 'disabled'}")

RS-485 Serial Control

# Initialize RS-485
dev.rs485_init(baudrate=9600)

# Transmit data
dev.rs485_tx(b"hello")

# Deinitialize
dev.rs485_deinit()

Getting Device Status

# Get full status
status = dev.get_device_status()

print(f"SoC temperature: {status.soc_temp_c}°C")
print(f"MCU temperature: {status.mcu_temp_c}°C")
print(f"Light sensor: {status.light_sensor}")
print(f"White light: {status.white_light_level}%")
print(f"IR LED: {'on' if status.ir_led_on else 'off'}")
print(f"IR-Cut: {status.ircut_mode}")
print(f"PTZ position: Pan={status.ptz_pan_pos}, Tilt={status.ptz_tilt_pos}")
print(f"Zoom position: {status.zoom_pos}")
print(f"Focus position: {status.focus_pos}")
print(f"Auto focus: {'enabled' if status.autofocus_enabled else 'disabled'}")
print(f"MCU version: {status.mcu_version}")
print(f"MCU uptime: {status.mcu_uptime_ms}ms")

Monitoring Device Events

# Subscribe to device events
for event in dev.subscribe_events():
    print(f"Event type: {event.type}")
    print(f"Timestamp: {event.timestamp_ns}")

    if event.type == DeviceEvent.EventType.GPIO_CHANGE:
        print(f"GPIO pin: {event.gpio_pin}, value: {event.gpio_value}")
    elif event.type == DeviceEvent.EventType.LIGHT_SENSOR_CHANGE:
        print(f"Light sensor value: {event.light_sensor_value}")
    elif event.type == DeviceEvent.EventType.TEMPERATURE_ALERT:
        print(f"Temperature alert: {event.temperature}°C")
    elif event.type == DeviceEvent.EventType.PTZ_MOVE_COMPLETE:
        print("PTZ move complete")
    elif event.type == DeviceEvent.EventType.FOCUS_COMPLETE:
        print("Focus complete")

Context Manager

# Use context manager for automatic connection management
with DeviceClient() as dev:
    dev.set_white_light(80)
    status = dev.get_device_status()
    print(f"Light: {status.white_light_level}%")

Error Handling

try:
    dev.set_white_light(150)  # Invalid value
except RuntimeError as e:
    print(f"Setting failed: {e}")

try:
    value = dev.gpio_get(99)  # Invalid pin
except RuntimeError as e:
    print(f"GPIO read failed: {e}")