# Fishinfo Python SDK

Python SDK for querying fishinfo telemetry data and parsing NMEA sentences.

**Note:** This SDK is for **querying** fishinfo data only. For data ingestion, use the [ingress-edge SDK](../ingress-edge/README.md).

## Features

- **Query API**: Rich query interface for telemetry data, aggregation, and downsampling
- **NMEA Parsing**: Parse NMEA 0183 sentences (GGA, RMC, VTG, etc.)
- **AIS Decoding**: Decode AIS messages (position reports, static data)
- **Pydantic Models**: Type-safe data models with automatic validation
- **Type Safety**: Full type hints for IDE support and mypy/pyright compatibility

## Installation

```bash
pip install fishinfo-sdk
# or
uv add fishinfo-sdk
```

## Quick Start

### Query Telemetry Data

```python
import asyncio
from datetime import datetime, timedelta, UTC
from uuid import UUID

from fishinfo import FishinfoConfig
from fishinfo.api import TelemetryApi

async def main():
    config = FishinfoConfig(
        base_url="https://fishinfo.example.com",
        api_key="vk_live_xxx...",
    )

    telemetry = TelemetryApi(config)

    # Query recent position data
    response = await telemetry.query(
        vessel_id=UUID("..."),
        data_type="position",
        start_time=datetime.now(UTC) - timedelta(hours=1),
        limit=100,
    )

    for point in response.data:
        print(f"{point.time}: {point.latitude}, {point.longitude}")

    # Get latest telemetry
    latest = await telemetry.get_latest(
        vessel_id=UUID("..."),
        data_type="position,speed",
    )

    # Get aggregated data
    from fishinfo import AggregationType

    agg = await telemetry.aggregate(
        vessel_id=UUID("..."),
        data_type="speed",
        start_time=datetime.now(UTC) - timedelta(days=1),
        end_time=datetime.now(UTC),
        interval="1 hour",
        agg_type=AggregationType.AVG,
    )

asyncio.run(main())
```

### NMEA Parsing

```python
from fishinfo.nmea import NMEAParser, NMEAConverter

parser = NMEAParser()
converter = NMEAConverter()

# Parse NMEA sentence
sentence = "$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,47.0,M,,*47"
gga = parser.parse(sentence)

print(f"Position: {gga.latitude}, {gga.longitude}")
print(f"Fix Quality: {gga.fix_quality}")
print(f"Satellites: {gga.satellites}")

# Convert to TelemetryPoint for API use
point = converter.to_telemetry_point(gga, vessel_id="...")
```

### AIS Decoding

```python
from fishinfo.nmea import AISDecoder

decoder = AISDecoder()

# Decode AIS message
payload = "13u@DP0P00Pd4aTN`88L4?v40000"
message = decoder.decode(payload)

if message.message_type == 1:  # Position Report
    print(f"MMSI: {message.mmsi}")
    print(f"Position: {message.latitude}, {message.longitude}")
    print(f"Speed: {message.speed_over_ground} knots")
    print(f"Course: {message.course_over_ground}°")
```

## Configuration

```python
from fishinfo import FishinfoConfig

# Using constructor
config = FishinfoConfig(
    base_url="https://fishinfo.example.com",
    api_key="vk_live_xxx...",
    timeout=60.0,
    max_retries=5,
)

# Using environment variables (FISHINFO_*)
# Set FISHINFO_BASE_URL, FISHINFO_API_KEY, etc.
config = FishinfoConfig()
```

### Configuration Options

| Option | Default | Description |
|--------|---------|-------------|
| `base_url` | (required) | Base URL of the fishinfo API |
| `api_key` | (required) | API key for authentication |
| `timeout` | 30.0 | HTTP request timeout in seconds |
| `max_retries` | 3 | Maximum retry attempts |
| `retry_delay` | 1.0 | Initial retry delay in seconds |
| `retry_backoff` | 2.0 | Backoff multiplier for retries |

## Data Models

All data models use Pydantic v2 for validation and serialization.

```python
from fishinfo import (
    TelemetryPoint,
    DataType,
    TelemetryResponse,
    AggregatedTelemetry,
    AggregationType,
)

# TelemetryResponse contains query results
response: TelemetryResponse = await telemetry.query(...)
for point in response.data:
    print(point.time, point.latitude, point.longitude)

# Aggregated data
agg: AggregatedTelemetry = await telemetry.aggregate(...)
for bucket in agg.data:
    print(bucket.time, bucket.value, bucket.count)
```

## NMEA Sentence Types

The SDK supports parsing these NMEA 0183 sentence types:

| Sentence | Description |
|----------|-------------|
| GGA | Global Positioning System Fix Data |
| RMC | Recommended Minimum Navigation Information |
| VTG | Track Made Good and Ground Speed |
| HDT | Heading True |
| HDG | Heading with Deviation & Variation |
| DBT | Depth Below Transducer |
| DPT | Depth of Water |
| MWV | Wind Speed and Angle |
| MTW | Mean Temperature of Water |

## Error Handling

```python
from fishinfo import (
    FishinfoError,
    ConfigError,
    ConnectionError,
    AuthenticationError,
    ValidationError,
    TimeoutError,
    ServerError,
)

try:
    response = await telemetry.query(...)
except AuthenticationError:
    print("Invalid API key")
except TimeoutError:
    print("Request timed out")
except ServerError as e:
    print(f"Server error: {e}")
```

## Environment Variables

```bash
FISHINFO_BASE_URL=https://fishinfo.example.com
FISHINFO_API_KEY=vk_live_xxx...
FISHINFO_TIMEOUT=30.0
FISHINFO_MAX_RETRIES=3
```

## Structured Data Helpers

For engine and catch data:

```python
from fishinfo.types import EngineData, CatchData

# Engine data structure
engine = EngineData(
    engine_id=1,
    rpm=1800,
    oil_pressure_kpa=450,
    coolant_temperature_c=78,
)

# Catch data structure
catch = CatchData(
    event="haul",
    species_code="COD",
    weight_kg=450.5,
)
```

## Data Ingestion

**This SDK does not handle data ingestion.** To send telemetry data to fishinfo, use the [ingress-edge SDK](../ingress-edge/README.md):

```python
# For ingestion, use ingress-edge:
from ingress_edge import IngressClient

async with IngressClient(...) as client:
    await client.send(point)
```

## License

MIT License - see LICENSE file.
