نظرة عامة
يتيح Naseem EIAQI API لأنظمة إدارة المباني (BMS) وأجهزة الاستشعار وتطبيقات الطرف الثالث إرسال بيانات جودة الهواء الداخلي مباشرةً، وحساب مؤشر EIAQI تلقائياً، وتخزين البيانات بحيث تلتزم بمتطلبات DM-HSD-GU141-IAQI2 لبلدية دبي.
https://naseem-eiaqi.com/api/v1Authorization: Bearer nsm-...نقاط النهاية المتاحة
/api/v1/readingsإرسال قراءة مستشعر واحدة أو دفعة
/api/v1/readingsاستعلام القراءات المخزنة
/api/v1/uploadرفع ملف CSV أو Excel بالجملة
/api/v1/exportتصدير ZIP كامل (مسؤول المؤسسة فقط)
المصادقة
تستخدم جميع نقاط النهاية مصادقة API key بصيغة Bearer token. أنشئ مفتاحك في لوحة التحكم: الإعدادات → مفاتيح API.
| الخاصية | التفاصيل |
|---|---|
| اسم الترويسة | Authorization |
| صيغة القيمة | Bearer nsm-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx |
| بادئة المفتاح | nsm- |
| الصلاحيات | readings:write · readings:read |
| نطاق المبنى | اختياري - يمكن تقييد المفتاح بمبنى واحد |
| الانتهاء | لا ينتهي أو بعد 30 / 90 / 365 يوماً |
تحذير: يُعرض المفتاح مرة واحدة فقط عند الإنشاء. خزّنه في متغيرات البيئة ولا تُدرجه في كود المصدر.
مثال على الطلب
Authorization: Bearer nsm-your-api-key-hereاستجابات الخطأ
| الحالة | السبب |
|---|---|
| 401 | المفتاح مفقود أو غير صالح أو ملغى |
| 403 | المفتاح موجود لكن لا يملك الصلاحية المطلوبة |
إرسال القراءات
/api/v1/readingsيتطلب: readings:writeأرسل قراءة واحدة أو دفعة (حتى 100 قراءة) لمستشعر واحد أو أكثر. يُحسب EIAQI فوراً لكل قراءة ويُخزّن في قاعدة البيانات.
قراءة واحدة - جميع المعاملات
{
"device_id": "SENSOR-001",
"recorded_at": "2026-06-01T10:00:00Z",
"readings": {
"pm25": 12.4,
"pm10": 28.0,
"co2": 650,
"co": 0.5,
"vocs": 0.18,
"hcho": 30,
"no2": 25,
"o3": 18,
"temperature": 23.5,
"humidity": 52
}
}قراءة جزئية - لا تحتاج كل المعاملات
يمكنك إرسال المعاملات التي يقيسها مستشعرك فقط. يحتسب EIAQI من المعاملات المتوفرة.
{
"device_id": "SENSOR-001",
"recorded_at": "2026-06-01T10:00:00Z",
"readings": {
"pm25": 12.4,
"co2": 650,
"temperature": 23.5,
"humidity": 52
}
}دفعة من القراءات
أرسل مصفوفة JSON بدلاً من كائن واحد. الحد الأقصى 100 قراءة في الطلب الواحد.
[
{
"device_id": "SENSOR-001",
"recorded_at": "2026-06-01T10:00:00Z",
"readings": { "pm25": 12.4, "co2": 650, "temperature": 23.5 }
},
{
"device_id": "SENSOR-002",
"recorded_at": "2026-06-01T10:00:00Z",
"readings": { "pm25": 18.2, "co2": 720, "temperature": 24.1 }
}
]استجابة - قراءة واحدة
{
"success": true,
"eiaqi": {
"score": 72,
"category": "good",
"dominant_pollutant": "pm25"
}
}استجابة - دفعة
{
"success": true,
"results": [
{
"device_id": "SENSOR-001",
"success": true,
"eiaqi": { "score": 72, "category": "good", "dominant_pollutant": "pm25" }
},
{
"device_id": "SENSOR-002",
"success": true,
"eiaqi": { "score": 68, "category": "good", "dominant_pollutant": "co2" }
}
]
}مرجع المعاملات داخل readings{}
| المعامل | المفتاح | الوحدة | النطاق | الوصف |
|---|---|---|---|---|
| PM₂.₅ | pm25 | μg/m³ | 0-1,000 | Fine particulate matter (≤ 2.5 μm aerodynamic diameter) |
| PM₁₀ | pm10 | μg/m³ | 0-1,000 | Coarse particulate matter (≤ 10 μm) |
| CO₂ | co2 | ppm | 400-5,000 | Carbon dioxide |
| CO | co | ppm | 0-50 | Carbon monoxide |
| VOCs | vocs | mg/m³ | 0-25 | Total volatile organic compounds (TVOC) |
| Formaldehyde | hcho | μg/m³ | 0-1,000 | Formaldehyde (HCHO / CH₂O) |
| NO₂ | no2 | μg/m³ | 0-2,000 | Nitrogen dioxide |
| O₃ | o3 | μg/m³ | 0-800 | Ozone |
| Temperature | temperature | °C | −10 to 60 | Indoor air temperature |
| Humidity | humidity | % | 0-100 | Relative humidity |
* جميع حقول readings اختيارية. يجب أن يحتوي الكائن على حقل واحد على الأقل.
رفع CSV / Excel
/api/v1/uploadيتطلب: readings:writeارفع ملف CSV أو TSV أو Excel يحتوي على بيانات تاريخية أو دفعات كبيرة. يكتشف النظام أسماء الأعمدة تلقائياً ويحوّل الوحدات.
| الخاصية | التفاصيل |
|---|---|
| نوع المحتوى | multipart/form-data |
| اسم الحقل | file |
| الصيغ المقبولة | .csv · .tsv · .xlsx |
| الحد الأقصى للصفوف | 100,000 |
| الحد الأقصى للحجم | 50 MB |
مثال curl
curl #86efac">"color:#fbbf24">-X POST https://naseem-eiaqi.com/api/v1/upload \
#86efac">"color:#fbbf24">-H "Authorization: Bearer nsm-your-api-key" \
#86efac">"color:#fbbf24">-F "file=@readings.csv"الاستجابة
{
"batch_id": "7f8a91c2-d3b4-4e5f-a6c7-8d9e0f1a2b3c",
"total_rows": 480,
"accepted": 478,
"rejected": 2,
"rejected_rows": [
{ "row": 47, "reason": "pm25 value 1800 exceeds maximum" },
{ "row": 312, "reason": "recorded_at is in the future" }
]
}استعلام القراءات
/api/v1/readingsيتطلب: readings:readاسترجع القراءات المخزنة. يجب توفير واحد على الأقل من: building_id أو zone_id أو sensor_id.
| المعامل | النوع | إلزامي | الوصف |
|---|---|---|---|
| building_id | uuid | مشروط | Building to query (required unless zone_id or sensor_id supplied) |
| zone_id | uuid | اختياري | Narrow results to a specific zone |
| sensor_id | uuid | اختياري | Narrow results to a specific sensor |
| from | ISO 8601 | اختياري | Start of time range (inclusive) |
| to | ISO 8601 | اختياري | End of time range (inclusive) |
| limit | integer | اختياري | Max results - default 100, max 500 |
مثال curl
curl #86efac">"https://naseem-eiaqi.com/api/v1/readings?building_id=BUILD-UUID&from=2026-06-01T00:00:00Z&limit=100" \
#86efac">"color:#fbbf24">-H "Authorization: Bearer nsm-your-api-key"الاستجابة
{
"success": true,
"count": 2,
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"sensor_id": "uuid",
"zone_id": "uuid",
"building_id": "uuid",
"recorded_at": "2026-06-01T10:00:00Z",
"pm25": 12.4,
"co2": 650,
"temperature": 23.5,
"humidity": 52
}
]
}استعلام النتائج
/api/v1/scoresيتطلب: readings:readقريباًاسترجع نتائج EIAQI المجمّعة حسب الفترة الزمنية. النتائج متاحة حالياً عبر لوحة التحكم وتقارير التصدير.
| المعامل | النوع | إلزامي | الوصف |
|---|---|---|---|
| building_id | uuid | اختياري | Filter by building |
| zone_id | uuid | اختياري | Filter by zone |
| period_type | enum | اختياري | hourly | eight_hour | daily | weekly | monthly |
| from | ISO 8601 | اختياري | Start of time range |
| to | ISO 8601 | اختياري | End of time range |
مثال على الاستجابة
{
"data": [
{
"id": "abc12345",
"building_id": "uuid",
"zone_id": "uuid",
"period_type": "hourly",
"period_start": "2026-06-01T10:00:00Z",
"period_end": "2026-06-01T11:00:00Z",
"eiaqi_score": 72,
"eiaqi_category": "good",
"dominant_pollutant": "pm25",
"sub_indices": {
"pm25": 68,
"co2": 45,
"vocs": 30
}
}
]
}أسماء الحقول المدعومة
عند رفع ملفات CSV أو Excel، يعترف النظام تلقائياً بعشرات الأسماء الشائعة لكل معامل. القائمة أدناه هي المرجع الكامل لمورّدي BMS.
| المعامل | المفتاح القانوني | الأسماء المقبولة |
|---|---|---|
| PM₂.₅ | pm25 | pm25pm2.5PM2.5pm2_5fine_particlesPM25pm2p5pm25_ugm3PM 2.5 |
| PM₁₀ | pm10 | pm10PM10pm1_0coarse_particlespm10_ugm3 |
| CO₂ | co2 | co2CO2carbon_dioxideco2_ppmco2ppmco2_levelcarbondioxide |
| CO | co | coCOcarbon_monoxideco_ppmcoppmco_mgm3 |
| VOCs | vocs | vocsvocVOCVOCstvocTVOCtotal_vocvolatile_organictvoc_ppbtvoc_mgm3 |
| Formaldehyde | hcho | hchoHCHOformaldehydeFormaldehydech2oCH2Ohcho_ugm3 |
| NO₂ | no2 | no2NO2nitrogen_dioxideno2_ugm3no2_ppbnitrogendioxide |
| O₃ | o3 | o3O3ozoneOzoneo3_ugm3o3_ppb |
| Temperature | temperature | temperaturetempTempTEMPtemp_cair_tempindoor_temptemperature_ctemperature_ftemp_ftempf |
| Humidity | humidity | humidityrhRHrelative_humidityhumhumidity_pctrh_pctrel_humidity |
أسماء الطابع الزمني المقبولة (recorded_at)
الوحدات والتحويل
تُطبَّق الوحدات القانونية وفقاً لـ DM-HSD-GU141-IAQI2. عند رفع الملفات، يكتشف النظام الوحدة من اسم العمود ويُحوّلها تلقائياً.
| المعامل | المفتاح | الوحدة القانونية | يُحوَّل تلقائياً من |
|---|---|---|---|
| PM₂.₅ | pm25 | μg/m³ | mg/m³ (×1,000) |
| PM₁₀ | pm10 | μg/m³ | mg/m³ (×1,000) |
| CO₂ | co2 | ppm | ppb (÷1,000) |
| CO | co | ppm | mg/m³ (×0.873 at 25 °C, 1 atm) |
| VOCs | vocs | mg/m³ | ppb (toluene MW 92.14), ppm, μg/m³ |
| Formaldehyde | hcho | μg/m³ | mg/m³ (×1,000), ppb |
| NO₂ | no2 | μg/m³ | mg/m³ (×1,000), ppb |
| O₃ | o3 | μg/m³ | mg/m³ (×1,000), ppb |
| Temperature | temperature | °C | °F → (F − 32) × 5/9; auto-detected from column name |
| Humidity | humidity | % | 0-1 fraction auto-scaled to 0-100 |
كيف يعمل الاكتشاف التلقائي
- →اسم عمود يحتوي على "mg" (بدون "ug") → يُعامل على أنه mg/m³
- →اسم عمود يحتوي على "ppb" → يُحوَّل لـ ppm أو μg/m³ حسب المعامل
- →درجة حرارة باسم يحتوي "fahrenheit" أو "_f" أو "°f" → تحويل فهرنهايت → سيلزيوس
- →رطوبة بقيمة ≤ 1.0 → تُضرب في 100 تلقائياً
رموز الأخطاء
جميع الأخطاء تُعاد بصيغة JSON مع حقل error وحقل details اختياري للتفاصيل الميدانية.
| الكود | الاسم | الوصف |
|---|---|---|
| 400 | Bad Request | Validation failed. Response includes a `details` array with per-field messages. |
| 401 | Unauthorized | API key is missing, invalid, or revoked. |
| 403 | Forbidden | Key exists but lacks the required scope (e.g. `readings:write`). |
| 404 | Not Found | The requested resource does not exist. |
| 422 | Unprocessable Entity | Schema validation passed but business rules failed (e.g. sensor not found, value out of range). |
| 429 | Too Many Requests | Rate limit exceeded (100 req/min per key). Check `Retry-After` header. |
| 500 | Internal Server Error | Transient server error. Retry with exponential backoff. |
400 - مثال
{
"success": false,
"error": "Validation failed",
"details": [
{
"field": "readings.pm25",
"message": "Value 1500 exceeds maximum 1000 μg/m³"
}
]
}401 - مثال
{
"success": false,
"error": "Invalid or expired API key"
}429 - مثال
{
"success": false,
"error": "Rate limit exceeded: 100 requests per minute"
}أمثلة برمجية
curl
إرسال قراءة مستشعر واحدة
curl #86efac">"color:#fbbf24">-X POST https://naseem-eiaqi.com/api/v1/readings \
#86efac">"color:#fbbf24">-H "Authorization: Bearer nsm-your-api-key" \
#86efac">"color:#fbbf24">-H "Content-Type: application/json" \
#86efac">"color:#fbbf24">-d @reading.jsonPython
مكتبة requests - pip install requests
import requests
API_KEY = "nsm-your-api-key"
BASE_URL = "https://naseem-eiaqi.com/api/v1"
headers = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
}
reading = {
"device_id": "SENSOR-001",
"recorded_at": "2026-06-01T10:00:00Z",
"readings": {
"pm25": 12.4,
"pm10": 28.0,
"co2": 650,
"temperature": 23.5,
"humidity": 52,
},
}
response = requests.post(
f"{BASE_URL}/readings",
json=reading,
headers=headers
)
print(response.json())Node.js
مدمج في Node.js 18+ (fetch native)
const API_KEY = 'nsm-your-api-key'
const BASE_URL = 'https:"color:#6b7280">//naseem-eiaqi.com/api/v1'
async function sendReading(data) {
const res = await fetch(BASE_URL + '/readings', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
})
if (!res.ok) throw new Error('HTTP ' + res.status)
return res.json()
}
"color:#6b7280">// Send a single reading
const result = await sendReading({
device_id: 'SENSOR-001',
recorded_at: new Date().toISOString(),
readings: { pm25: 12.4, co2: 650, temperature: 23.5 },
})
console.log(result)n8n
استخدم عقدة "HTTP Request" مع الإعدادات التالية. عيّن البيانات من الحقول السابقة في سير العمل باستخدام تعبيرات n8n.
"color:#6b7280">// HTTP Request node - configure as follows:
Method: POST
URL: https:"color:#6b7280">//naseem-eiaqi.com/api/v1/readings
"color:#6b7280">// Headers tab
Authorization: Bearer nsm-your-api-key
Content-Type: application/json
"color:#6b7280">// Body tab - select "JSON (Raw)"
{
"device_id": "{{ $json.device_id }}",
"recorded_at": "{{ $now.toISO() }}",
"readings": {
"pm25": {{ $json.pm25 }},
"co2": {{ $json.co2 }},
"temperature": {{ $json.temperature }},
"humidity": {{ $json.humidity }}
}
}هل تحتاج مساعدة؟
أنشئ مفتاح API من الإعدادات وجرّب أول طلب خلال دقيقتين. للدعم الفني، تواصل معنا عبر hello@naseem-eiaqi.com.