Metadata-Version: 2.3
Name: alarm-events-client
Version: 0.1.2
Summary: Python client for the Equinor IOC IIOT Alarm and Events API
Requires-Dist: eq-api-connector>=1.2.1,<2
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
authentication supported by `eq-api-connector` (delegated `user_impersonation`
by default).
Python 3.10 or later is required by the connector.

## Install

```bash
pip install alarm-events-client
```

## Authentication

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

Authentication defaults to delegated `user_impersonation`; the first request may
prompt for browser sign-in. `client_secret`, `user_assertion`, and other
`eq-api-connector` authentication inputs can be supplied at construction. Keep
secrets and tokens out of source control and logs; this SDK does not use an APIM
key.

## 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), events[0].source_name)
```

Each result from `get_all_events()` is an `Event` instance. Known API fields are
available as snake_case attributes (for example, `event.timestamp`,
`event.priority`, and `event.other_fields`), while the original API field names
remain available by key (for example, `event["sourceName"]`). Use
`event.to_dict()` for a plain dictionary or `event.to_json(indent=2)` for a JSON
string. Pass `as_dict=True` to `get_all_events()` to return plain dictionaries
instead.

### Facility and event demo

Run the demo to fetch events and save them to `ae_events.json`:

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

It checks `JCA`, `JSV`, and `GRA` over a 4-hour window ending 24 hours before
the run. Edit the constants in `examples/facility_events_demo.py` to change its
settings.

Pass supported `eq-api-connector` authentication inputs such as `client_secret`
or `user_assertion` to `AlarmEventsClient` as needed. The API base URL is fixed;
TLS verification remains enabled, redirects are rejected, and every request has
a timeout (default 60 s).

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. A missing or null `totalCount` is
treated as unknown; in that case, retrieval completeness is verified by
following continuation tokens through the terminal page. Invalid counts or a
count smaller than the page's item list raise `InvalidResponseError`.

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`; its
  `items` are raw dictionaries and `.events` converts them on demand.
- `get_all_events()` follows continuation tokens, verifies `totalCount` when
  supplied, 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 from the connector. 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 pre-commit install
uv run pre-commit run --all-files
uv build
```

The installed hook runs pytest, Black, isort, flake8, and mypy when source or
test files change.

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.
