Metadata-Version: 2.4
Name: axis
Version: 73
Summary: A Python library for communicating with devices from Axis Communications
Author-email: Robert Svensson <Kane610@users.noreply.github.com>
License: MIT
Project-URL: Source Code, https://github.com/Kane610/axis
Project-URL: Bug Reports, https://github.com/Kane610/axis/issues
Project-URL: Forum, https://community.home-assistant.io/t/axis-camera-component/
Keywords: axis,vapix,homeassistant
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Home Automation
Requires-Python: >=3.14.0
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: aiohttp>=3.12
Requires-Dist: faust-cchardet>=2.1.18
Requires-Dist: orjson>3.9
Requires-Dist: packaging>23
Requires-Dist: tomli>=2.0
Requires-Dist: tomli-w>=1.0
Requires-Dist: xmltodict>=0.13.0
Requires-Dist: zeroconf==0.150.0
Provides-Extra: requirements
Requires-Dist: aiohttp==3.14.1; extra == "requirements"
Requires-Dist: orjson==3.11.9; extra == "requirements"
Requires-Dist: packaging==26.2; extra == "requirements"
Requires-Dist: xmltodict==1.0.4; extra == "requirements"
Requires-Dist: zeroconf==0.150.0; extra == "requirements"
Provides-Extra: requirements-test
Requires-Dist: mypy==2.1.0; extra == "requirements-test"
Requires-Dist: pytest==9.1.1; extra == "requirements-test"
Requires-Dist: pytest-aiohttp==1.1.1; extra == "requirements-test"
Requires-Dist: pytest-asyncio==1.4.0; extra == "requirements-test"
Requires-Dist: pytest-cov==7.1.0; extra == "requirements-test"
Requires-Dist: ruff==0.15.20; extra == "requirements-test"
Requires-Dist: types-xmltodict==v1.0.1.20260518; extra == "requirements-test"
Provides-Extra: requirements-dev
Requires-Dist: pre-commit==4.6.0; extra == "requirements-dev"
Dynamic: license-file

# axis

Python project to set up a connection towards Axis Communications devices and to subscribe to specific events on the metadatastream.

## Development setup

`uv` is required for development setup:

```bash
uv python install 3.14
uv sync --python 3.14 --all-extras
```

Or run the bootstrap script, which installs `uv` if needed and provisions Python 3.14 automatically:

```bash
./setup.sh
```

Dependencies are locked via `uv.lock`. Regenerate lock data when dependency inputs change:

```bash
uv lock
```

Run checks with `uv`:

```bash
uv run ruff check .
uv run ruff format --check .
uv run mypy axis
uv run pytest
```

Initial `ty` support is configured as an opt-in check and does not replace `mypy`:

```bash
uvx ty check
```

## CLI navigation contract

The interactive CLI in `axis/cli/` follows a menu-first model.
Router runtime is the only interactive execution path.

- Canonical route graph: `main -> devices -> device_operations -> {api|events|accounts}`.
- `device_operations` routes to feature submenus using router navigation, not direct feature command execution.
- Feature nodes (`api`, `events`, `accounts`) own their local menu layout and selected-device context rendering.
- `b` always navigates to `parent_id`; `e` always exits.

To keep the CLI reusable boundary clear:

- CLI concerns (menu keys, prompts, render text, router state) stay in `axis/cli/`.
- `axis/interfaces/` and `axis/models/` must remain presentation-agnostic and reusable by non-CLI consumers.
- Domain layers return typed data/errors; CLI adapters map them to user-facing text and flow.

## Initialization architecture

Vapix initialization is phase-based and driven by handler metadata:

- `API_DISCOVERY`: handlers initialized after API discovery.
- `PARAM_CGI_FALLBACK`: handlers that may initialize from parameter support when not listed in discovery.
- `APPLICATION`: handlers initialized after applications are loaded.

Handlers declare phase membership through `handler_groups` and may customize phase eligibility through `should_initialize_in_group`.

## Request and response typing

`Vapix.api_request()` is the single typed request entrypoint.

- Request models declare their decode contract with `ApiRequest[ResponseT]`.
- Every `ApiRequest` subclass must set `response_type` explicitly.
- Decoded/read requests use their concrete response model as `response_type`.
- Write-style requests use `BytesResponse` as `response_type`.

Examples:

```python
@dataclass
class ListApisRequest(ApiRequest[GetAllApisResponse]):
	response_type = GetAllApisResponse


@dataclass
class SetPortsRequest(ApiRequest[ApiResponse[bytes]]):
	response_type = BytesResponse
```

Handler methods may unwrap `.data` when they intentionally preserve a bytes-returning boundary.

Example fallback policy:

- `LightHandler` participates in both `API_DISCOVERY` and `PARAM_CGI_FALLBACK`.
- In `PARAM_CGI_FALLBACK`, it initializes only when not listed in API discovery and listed in parameters.

## Event Instance Model Notes

`EventInstance` keeps `name` as the raw device-provided `NiceName` value.

`EventInstance.source` and `EventInstance.data` are typed containers (`EventInstanceSource` and `EventInstanceData`) built from `SimpleItemInstance` payloads.

For compatibility with integrations that still need the historical raw payload shape, `EventInstance` also exposes:

- `raw_source`: returns `{}` or a dict or a list of dicts.
- `raw_data`: returns `{}` or a dict or a list of dicts.
