Metadata-Version: 2.3
Name: alarm-events-client
Version: 0.1.0
Summary: Python client for the Equinor IOC IIOT Alarm and Events API
Requires-Dist: eq-api-connector>=1.2.0,<2
Requires-Dist: requests>=2.31,<3
Requires-Python: >=3.10
Project-URL: Repository, https://github.com/equinor/ioc-ae-sdk
Project-URL: Issues, https://github.com/equinor/ioc-ae-sdk/issues
Description-Content-Type: text/markdown

# Alarm and Events SDK

Read-only Python library for the Equinor IOC IIOT Alarm and Events API, using delegated
**user_impersonation** authentication through `eq-api-connector`.
Python 3.10 or later is required by the connector.

## Install

```bash
pip install "git+https://github.com/equinor/ioc-ae-sdk.git"
```

GitHub access to this internal repository is required. This package is not
published to PyPI yet; its intended distribution name is `alarm-events-client`.

## Authentication

| Setting | Default |
| --- | --- |
| API application | `omnia-iiot-ae-api-production` |
| API resource ID | `6df18f43-f499-4470-9d94-52b01698620d` |
| Delegated scope | `6df18f43-f499-4470-9d94-52b01698620d/user_impersonation` |
| Calling public-client application | `IOC-monitoring-sdk` |
| Calling client ID | `98fe146b-2687-4db9-9c84-45f4cd9063af` |
| Tenant ID | `3aa4a235-b6e2-48d5-9195-7fcf05b459b0` |
| Base URL | `https://api.gateway.equinor.com/iiot/ae/v1` |

The application names, IDs and enabled delegated permission were checked against
Entra service-principal metadata on 2026-10-29. IDs are configuration, not secrets.
The calling app declares this exact delegated permission and includes
`http://localhost` among its public-client redirect URIs.
The calling app needs delegated permission/consent for this API, a suitable
public-client redirect configuration, and the user needs authorized API access
(including `AE.Read` where required). Metadata discovery alone does not verify
consent or user access.

`eq-api-connector` owns MSAL login, token renewal and its credential cache.
The first request may open interactive browser login; subsequent requests reuse
the connector's authentication. No client secret, application-token fallback,
Azure CLI token, or APIM subscription key is used by this SDK.
Do not print tokens or commit credential caches. Do not enable HTTP debug logging
where Authorization headers could be captured.

## Usage

```python
from alarm_events import AlarmEventsClient

with AlarmEventsClient() as client:
    facilities = client.list_facilities()
    events = client.get_all_events(
        "TROA",
        source_name="*",
        start="2026-10-28T00:00:00Z",
        end="2026-10-28T01:00:00Z",
    )
    print(len(events))
```

### Facility and event demo

Run the demo to check STID facility codes against `GET /sources` and save
matching event records to a JSON file:

```bash
uv run python examples/facility_events_demo.py
```

By default, it checks `JCA`, `JSV`, and `GRA`, then retrieves events from the
last 24 hours for supported facilities. The example uses a wildcard source,
priorities 1 and 2, unacknowledged and active state filters, descending sort,
and includes other event fields. It follows continuation tokens and verifies
completeness before writing `ae_events.json`. A supported facility can have
zero events matching the selected time window and filters.

Override the facility codes, time window, or output path with command-line
options:

```bash
uv run python examples/facility_events_demo.py \
  --facilities JCA JSV GRA --hours 48 --output output/ae_events.json
```

Edit `EVENT_FILTERS` in `examples/facility_events_demo.py` to change the event
filters.

Override `client_id`, `tenant_id`, `scope`, or `user_name` at construction if needed.
Only explicit `/user_impersonation` scopes are accepted. Optional `base_url` and
`timeout` overrides support alternate HTTPS deployments. Only set `base_url` to a
trusted API: it receives the delegated bearer token. TLS verification remains
enabled, redirects are rejected, and every request has a timeout (default 60 s).

### Single-page filtering

```python
with AlarmEventsClient() as client:
    page = client.get_events(
        "TROA",
        start="2026-10-28T00:00:00Z",
        end="2026-10-28T01:00:00Z",
        source_name="*",
        priority=[1, 2],
        acked_state=False,
        sort="Desc",
        include_other_fields=True,
        limit=1000,
    )
    # page.items, page.total_count, page.continuation_token
```

All three event-reading methods accept `description`, `event_type`,
`event_category`, `message`, `severity`, `priority`, `condition`, `alarm_state`,
`acked_state`, `active_state`, `suppressed_or_shelved`, `node`, `process_area`,
`actiontime`, `include_other_fields`, and `sort`, mapping to the API's parameters.
Filters other than `sort` and `include_other_fields` accept a scalar or a
list/tuple of values; sequences are sent as repeated query parameters, not
comma-separated strings. Priority and severity accept int32 values (numeric
strings remain supported); state flags require booleans. `actiontime` values
require timezone offsets and are normalized to UTC.
`sort` is sent as the schema's `Asc` or `Desc` enum; lowercase input is also
accepted. Filters are preserved on every page.

Single-page results may be incomplete. `get_events` additionally accepts
`continuation_token` for manual paging.

The read surface was checked against the supplied `iiot-ae-api-v1.yaml`
OpenAPI 3.0.1 definition. Its limit schema allows 1 through 100,000, despite a
shorter conflicting description. The SDK follows that schema and retains
the conservative completeness checks below. Although the response schema
allows null collections/counts, these cannot establish completeness and raise
`InvalidResponseError` rather than silently becoming empty successful reads.

Both time bounds are required and must be timezone-aware `datetime` objects or
ISO-8601 strings with an offset; they are normalized to UTC. The facility is a
path code, and `sourceName` is always supplied (`*` by default).
Unknown keyword arguments raise `TypeError`, preventing misspelled time filters
from silently reading the wrong period. Events retain the API's original fields
and nulls; no alarm interpretation or personal-data transformation is performed.

### Completeness and errors

- `list_facilities()` calls `GET /sources`, not `/facilities`.
- `get_events()` calls `GET /events/{facility}` and returns an `EventPage`.
- `get_all_events()` follows continuation tokens, verifies `totalCount`, and
  returns a complete list or raises `IncompleteResultError`.
- `iter_event_pages()` verifies completeness only if consumed to exhaustion.
- Repeated tokens, changing counts, missing items, the `max_pages` bound, and
  the service's 100,000-event ceiling raise `IncompleteResultError`. Narrow the
  window or source filter rather than assuming a capped result is complete.
- HTTP errors (including redirects) raise `AlarmEventsAPIError` with
  `status_code` and `trace_id` when available. Error bodies and credentials
  are not included in exception messages.
- HTTP 401 requires checking login, scope and consent; HTTP 403 requires checking
  access. Neither is silently converted into an empty result.
- Network errors/timeouts propagate as `requests` exceptions. Malformed JSON or
  response envelopes raise `InvalidResponseError`. Requests are not automatically
  retried; retry transient failures at the caller's policy boundary.

Streaming subscriptions are intentionally excluded: the service does not allow
delegated `user_impersonation` tokens for realtime subscriptions. The published
server-side count route has been observed returning 404 and is not exposed here.
Count the completely retrieved list when needed.

## Development

```bash
uv sync --locked
uv run pytest --cov=alarm_events --cov-report=term-missing
uv run black --check src tests
uv run isort --check-only src tests
uv run flake8 src tests
uv run mypy
uv build
```

All automated tests use fake authentication and HTTP responses. They do not
require sign-in or call the API. A successful offline test run is not proof of
live access; validate login and a bounded facilities/event read in your own
authorized environment. Keep real event data and tokens out of the repository.
