# مستند API سنسورها برای فرانت

این فایل قرارداد پیشنهادی/هدف برای endpointهای سنسوری زیر است و بر اساس نیاز اعلام‌شده تهیه شده است:

- `GET /api/sensor-7-in-1/summary/`
- `GET /api/sensors/comparison-chart/`
- `GET /api/sensors/radar-chart/`
- `GET /api/sensors/values-list/`
- `GET /api/sensor-external-api/logs/`

نکته مهم:

- این سند بر اساس نیاز محصول و قرارداد موردنظر فرانت نوشته شده است.
- در این قرارداد دیگر `physical_device_uuid` از فرانت گرفته نمی‌شود.
- مبنای جست‌وجو فقط `farm_uuid` است.
- backend باید با استفاده از `farm_uuid`، رکورد مزرعه را پیدا کند و اولین سنسور خاک را به‌عنوان سنسور مبنا انتخاب کند.
- اگر `range` ارسال نشود، backend باید بدون خطا مقدار پیش‌فرض `7` روز را در نظر بگیرد.

---

## 1) قواعد عمومی

### آدرس پایه

- پیشوند تمام مسیرها: `/api/`

### فرمت پاسخ

همه endpointها بهتر است envelope استاندارد زیر را برگردانند:

```json
{
  "code": 200,
  "msg": "success",
  "data": {}
}
```

### پارامترهای مشترک

- `farm_uuid` — `uuid string` — الزامی
- `range` — `integer` — اختیاری — پیش‌فرض: `7`

### رفتار `range`

- اگر `range` ارسال نشده باشد: backend باید `7` را استفاده کند.
- اگر `range` کمتر از `1` باشد: بهتر است `400` برگردد.
- اگر `range` خیلی بزرگ باشد: پیشنهاد می‌شود backend آن را محدود کند، مثلا حداکثر `90`.

نمونه:

- `/api/sensor-7-in-1/summary/?farm_uuid=11111111-1111-1111-1111-111111111111`
- `/api/sensors/comparison-chart/?farm_uuid=11111111-1111-1111-1111-111111111111&range=30`

### منطق انتخاب سنسور

با دریافت `farm_uuid`:

1. رکورد `farm_data.SensorData` پیدا می‌شود.
2. از `sensor_payload` اولین سنسور خاک انتخاب می‌شود.
3. اگر چند سنسور موجود باشد، اولویت پیشنهادی:
   - اولین کلیدی که با `sensor-7` یا `sensor-7-in-1` شروع می‌شود
   - اگر نبود، اولین block معتبر از نوع object
4. همان سنسور برای ساخت summary، chart و values استفاده می‌شود.

### خطاهای مشترک

#### 400 — ورودی نامعتبر

```json
{
  "code": 400,
  "msg": "داده نامعتبر.",
  "data": {
    "farm_uuid": [
      "This field is required."
    ]
  }
}
```

یا:

```json
{
  "code": 400,
  "msg": "داده نامعتبر.",
  "data": {
    "range": [
      "range باید عددی بزرگ‌تر از صفر باشد."
    ]
  }
}
```

#### 404 — مزرعه یا سنسور پیدا نشد

```json
{
  "code": 404,
  "msg": "farm یا سنسور خاک یافت نشد.",
  "data": null
}
```

#### 200 — بدون داده کافی

اگر مزرعه وجود داشته باشد ولی history کافی برای بازه در دسترس نباشد، پیشنهاد می‌شود endpoint به‌جای خطا، پاسخ موفق با `data` خالی یا حداقلی برگرداند.

---

## 2) GET /api/sensor-7-in-1/summary/

### هدف

نمایش خلاصه سریع آخرین وضعیت سنسور خاک انتخاب‌شده برای یک مزرعه.

### Query Params

- `farm_uuid` — `uuid string` — الزامی
- `range` — `integer` — اختیاری — پیش‌فرض `7`

### منطق پاسخ

- آخرین reading سنسور انتخاب‌شده نمایش داده می‌شود.
- اگر داده historical موجود باشد، trend نسبت به بازه `range` محاسبه می‌شود.
- این endpoint مناسب hero cards و summary cards فرانت است.

### پاسخ موفق نمونه

```json
{
  "code": 200,
  "msg": "success",
  "data": {
    "farm_uuid": "11111111-1111-1111-1111-111111111111",
    "sensor_key": "sensor-7-1",
    "range": 7,
    "last_updated_at": "2026-04-29T10:20:00Z",
    "summary": {
      "soil_moisture": {
        "label": "رطوبت خاک",
        "value": 31.2,
        "unit": "%",
        "trend": "up",
        "change": 2.1,
        "change_unit": "%"
      },
      "soil_temperature": {
        "label": "دمای خاک",
        "value": 22.8,
        "unit": "°C",
        "trend": "stable",
        "change": 0.3,
        "change_unit": "°C"
      },
      "soil_ph": {
        "label": "pH خاک",
        "value": 6.9,
        "unit": "",
        "trend": "down",
        "change": -0.1,
        "change_unit": ""
      },
      "electrical_conductivity": {
        "label": "هدایت الکتریکی",
        "value": 1.4,
        "unit": "mS/cm",
        "trend": "stable",
        "change": 0.0,
        "change_unit": "mS/cm"
      },
      "nitrogen": {
        "label": "نیتروژن",
        "value": 28.0,
        "unit": "mg/kg",
        "trend": "up",
        "change": 1.8,
        "change_unit": "mg/kg"
      },
      "phosphorus": {
        "label": "فسفر",
        "value": 14.5,
        "unit": "mg/kg",
        "trend": "stable",
        "change": 0.4,
        "change_unit": "mg/kg"
      },
      "potassium": {
        "label": "پتاسیم",
        "value": 21.7,
        "unit": "mg/kg",
        "trend": "down",
        "change": -0.9,
        "change_unit": "mg/kg"
      }
    }
  }
}
```

### فیلدهای خروجی

- `farm_uuid`: شناسه مزرعه
- `sensor_key`: کلید سنسور انتخاب‌شده از `sensor_payload`
- `range`: بازه واقعی استفاده‌شده
- `last_updated_at`: زمان آخرین reading یا آخرین به‌روزرسانی
- `summary`: آبجکت شامل KPIهای اصلی

### ساختار هر KPI

- `label`: عنوان فارسی
- `value`: آخرین مقدار
- `unit`: واحد
- `trend`: یکی از `up | down | stable | unknown`
- `change`: اختلاف با ابتدای بازه یا میانگین بازه
- `change_unit`: واحد اختلاف

---

## 3) GET /api/sensors/comparison-chart/

### هدف

برگرداندن داده chart مقایسه‌ای برای چند پارامتر سنسور در طول بازه زمانی.

### Query Params

- `farm_uuid` — `uuid string` — الزامی
- `range` — `integer` — اختیاری — پیش‌فرض `7`

### منطق پاسخ

- فقط یک سنسور مبنا از روی `farm_uuid` انتخاب می‌شود.
- برای همان سنسور، سری‌های چند متریک در طول بازه برگردانده می‌شود.
- فرانت می‌تواند آن را به line chart یا multi-series chart تبدیل کند.

### پاسخ موفق نمونه

```json
{
  "code": 200,
  "msg": "success",
  "data": {
    "farm_uuid": "11111111-1111-1111-1111-111111111111",
    "sensor_key": "sensor-7-1",
    "range": 7,
    "categories": [
      "2026-04-23",
      "2026-04-24",
      "2026-04-25",
      "2026-04-26",
      "2026-04-27",
      "2026-04-28",
      "2026-04-29"
    ],
    "series": [
      {
        "key": "soil_moisture",
        "label": "رطوبت خاک",
        "unit": "%",
        "data": [29.1, 28.7, 30.4, 30.0, 31.1, 31.0, 31.2]
      },
      {
        "key": "soil_temperature",
        "label": "دمای خاک",
        "unit": "°C",
        "data": [21.4, 21.8, 22.0, 22.1, 22.2, 22.6, 22.8]
      },
      {
        "key": "electrical_conductivity",
        "label": "هدایت الکتریکی",
        "unit": "mS/cm",
        "data": [1.2, 1.3, 1.3, 1.4, 1.4, 1.4, 1.4]
      }
    ]
  }
}
```

### فیلدهای خروجی

- `categories`: برچسب‌های محور زمان
- `series`: آرایه سری‌ها

### ساختار هر سری

- `key`: کلید متریک
- `label`: نام نمایشی
- `unit`: واحد
- `data`: آرایه مقادیر هم‌طول با `categories`

---

## 4) GET /api/sensors/radar-chart/

### هدف

دادن داده مناسب radar chart برای مقایسه هم‌زمان وضعیت متریک‌های اصلی سنسور.

### Query Params

- `farm_uuid` — `uuid string` — الزامی
- `range` — `integer` — اختیاری — پیش‌فرض `7`

### منطق پاسخ

- برای هر متریک، یک مقدار خلاصه از بازه ساخته می‌شود؛ مثلا:
  - آخرین مقدار
  - میانگین بازه
  - یا score نرمال‌شده 0 تا 100
- برای radar chart پیشنهاد می‌شود score نهایی نرمال‌شده برگردانده شود.

### پاسخ موفق نمونه

```json
{
  "code": 200,
  "msg": "success",
  "data": {
    "farm_uuid": "11111111-1111-1111-1111-111111111111",
    "sensor_key": "sensor-7-1",
    "range": 7,
    "labels": [
      "رطوبت خاک",
      "دمای خاک",
      "pH خاک",
      "هدایت الکتریکی",
      "نیتروژن",
      "فسفر",
      "پتاسیم"
    ],
    "series": [
      {
        "name": "وضعیت فعلی",
        "data": [72, 64, 81, 58, 69, 61, 74]
      }
    ],
    "raw_metrics": [
      {
        "key": "soil_moisture",
        "label": "رطوبت خاک",
        "value": 31.2,
        "unit": "%",
        "score": 72
      },
      {
        "key": "soil_temperature",
        "label": "دمای خاک",
        "value": 22.8,
        "unit": "°C",
        "score": 64
      },
      {
        "key": "soil_ph",
        "label": "pH خاک",
        "value": 6.9,
        "unit": "",
        "score": 81
      }
    ]
  }
}
```

### فیلدهای خروجی

- `labels`: برچسب‌های radar
- `series`: داده آماده برای chart
- `raw_metrics`: داده خام برای tooltip و جزئیات بیشتر

---

## 5) GET /api/sensors/values-list/

### هدف

برگرداندن لیست tabular از مقادیر سنسور برای بازه زمانی، مناسب table یا export.

### Query Params

- `farm_uuid` — `uuid string` — الزامی
- `range` — `integer` — اختیاری — پیش‌فرض `7`

### پاسخ موفق نمونه

```json
{
  "code": 200,
  "msg": "success",
  "data": {
    "farm_uuid": "11111111-1111-1111-1111-111111111111",
    "sensor_key": "sensor-7-1",
    "range": 7,
    "count": 3,
    "items": [
      {
        "recorded_at": "2026-04-29T10:20:00Z",
        "soil_moisture": 31.2,
        "soil_temperature": 22.8,
        "soil_ph": 6.9,
        "electrical_conductivity": 1.4,
        "nitrogen": 28.0,
        "phosphorus": 14.5,
        "potassium": 21.7
      },
      {
        "recorded_at": "2026-04-28T10:20:00Z",
        "soil_moisture": 31.0,
        "soil_temperature": 22.6,
        "soil_ph": 7.0,
        "electrical_conductivity": 1.4,
        "nitrogen": 27.5,
        "phosphorus": 14.1,
        "potassium": 22.1
      },
      {
        "recorded_at": "2026-04-27T10:20:00Z",
        "soil_moisture": 31.1,
        "soil_temperature": 22.2,
        "soil_ph": 7.0,
        "electrical_conductivity": 1.3,
        "nitrogen": 27.2,
        "phosphorus": 14.0,
        "potassium": 22.6
      }
    ]
  }
}
```

### فیلدهای خروجی

- `count`: تعداد رکوردها
- `items`: لیست ردیف‌ها
- هر ردیف شامل timestamp و مقادیر متریک‌ها

### رفتار پیشنهادی

- ترتیب رکوردها: جدید به قدیم
- اگر داده تاریخی نداریم ولی آخرین payload فعلی موجود است، حداقل یک item با آخرین وضعیت برگردانده شود

---

## 6) GET /api/sensor-external-api/logs/

### هدف

نمایش لاگ‌های مربوط به سینک یا واکشی داده سنسور از API بیرونی، مناسب صفحه monitoring یا audit.

### Query Params

- `farm_uuid` — `uuid string` — الزامی
- `range` — `integer` — اختیاری — پیش‌فرض `7`

### توضیح دامنه

این endpoint برای نمایش لاگ‌های integration است، نه لزوما readingهای سنسور.

اگر backend هنوز لاگ جداگانه برای external sensor sync نداشته باشد، پیشنهاد می‌شود ساختار زیر مبنای پیاده‌سازی قرار بگیرد.

### پاسخ موفق نمونه

```json
{
  "code": 200,
  "msg": "success",
  "data": {
    "farm_uuid": "11111111-1111-1111-1111-111111111111",
    "range": 7,
    "count": 4,
    "items": [
      {
        "id": 104,
        "status": "success",
        "source": "sensor-external-api",
        "sensor_key": "sensor-7-1",
        "requested_at": "2026-04-29T10:20:00Z",
        "finished_at": "2026-04-29T10:20:01Z",
        "duration_ms": 842,
        "http_status": 200,
        "message": "داده سنسور با موفقیت واکشی شد."
      },
      {
        "id": 103,
        "status": "error",
        "source": "sensor-external-api",
        "sensor_key": "sensor-7-1",
        "requested_at": "2026-04-28T10:20:00Z",
        "finished_at": "2026-04-28T10:21:00Z",
        "duration_ms": 60000,
        "http_status": 504,
        "message": "Timeout هنگام واکشی داده سنسور."
      }
    ]
  }
}
```

### فیلدهای خروجی

- `status`: یکی از `success | error | timeout | partial`
- `source`: نام provider یا سرویس خارجی
- `requested_at`: زمان شروع درخواست
- `finished_at`: زمان پایان
- `duration_ms`: مدت زمان
- `http_status`: وضعیت HTTP سرویس بیرونی
- `message`: پیام خلاصه برای UI

---

## 7) رفتار پیشنهادی در نبود `range`

در همه endpointهای این سند:

- اگر `range` ارسال نشده باشد:

```json
{
  "range": 7
}
```

باید به‌صورت implicit استفاده شود و endpoint نباید خطای validation برگرداند.

---

## 8) رفتار پیشنهادی در نبود `physical_device_uuid`

فرانت نباید `physical_device_uuid` ارسال کند.

backend باید:

- فقط `farm_uuid` را بگیرد
- سنسور را از روی `sensor_payload` یا mapping داخلی انتخاب کند
- `sensor_key` نهایی را در پاسخ برگرداند تا فرانت بداند داده از کدام سنسور آمده است

---

## 9) پیشنهاد استاندارد برای متریک‌ها

برای هماهنگی فرانت و بک، بهتر است حداقل این کلیدها در endpointها پشتیبانی شوند:

- `soil_moisture`
- `soil_temperature`
- `soil_ph`
- `electrical_conductivity`
- `nitrogen`
- `phosphorus`
- `potassium`

### واحدهای پیشنهادی

- `soil_moisture` → `%`
- `soil_temperature` → `°C`
- `soil_ph` → بدون واحد
- `electrical_conductivity` → `mS/cm`
- `nitrogen` → `mg/kg`
- `phosphorus` → `mg/kg`
- `potassium` → `mg/kg`

---

## 10) پیشنهاد برای وضعیت‌های فرانت

### loading

- هنگام request، فرانت skeleton یا spinner نشان دهد

### empty

- اگر `items: []` یا `series: []` برگشت:
  - پیام مناسب مثل `داده‌ای برای این بازه ثبت نشده است.` نمایش داده شود

### partial

- اگر بعضی متریک‌ها `null` باشند:
  - chart فقط seriesهای موجود را نمایش دهد
  - در table برای فیلدهای خالی `—` نمایش داده شود

---

## 11) جمع‌بندی قرارداد

برای این 5 endpoint، قرارداد موردنیاز فرانت به‌صورت خلاصه:

- ورودی اصلی فقط `farm_uuid`
- `physical_device_uuid` حذف شود
- `range` اختیاری باشد
- اگر `range` نیامد، مقدار پیش‌فرض `7` در نظر گرفته شود
- backend اولین سنسور خاک مزرعه را انتخاب کند
- `sensor_key` انتخاب‌شده در response برگردانده شود
- responseها envelope استاندارد `code/msg/data` داشته باشند

