Skip to main content

Device Data (v1)

GET /api/user/deviceData returns aggregated IoT sensor readings for a given date range and category. It is the primary endpoint for dashboard charts and device data tables.


GET /api/user/deviceData

GET /api/user/deviceData?date1=09/11/2024&category=sourceCategory&type=DAY
Authorization: Bearer <access_token>

Parameters

ParameterTypeRequiredDefaultDescription
date1stringYesDD/MM/YYYY — start date
date2stringNoDD/MM/YYYY — end of range for comparison queries
categorystringYesOne of sourceCategory, stockCategory, qualityCategory, energyCategory
subCategorystringNoSub-category ID within the selected category
divisionFactorintNo1000Divisor applied to raw readings before display. Sent as 1000 if omitted
typestringYesAggregation pattern from PatternType enum: HOUR, DAY, MONTH
includeTodaybooleanNotrueWhen true, fetches the latest live reading for the current period

Validation

warning

Validation runs before any data fetching. The first missing required field short-circuits the request.

ConditionError codeMessage
date1 not provided1403`date1` missing
type not provided1403`type` missing
category not provided1403`category` missing
category value not in current_user['categories']1401Invalid category, `{category}` doesn't exist

Date Conversion

DeviceDataRouteHandler.get_date1_and_date2() converts the DD/MM/YYYY request string to a UTC timestamp pair before any Cosmos DB query runs. The conversion is shift-aware — every industry defines a shift start time and an optional daysOffset for industries whose shift spans midnight.

info

Steps:

  1. days_offset = int(current_user['daysOffset'])
  2. shift is read from industry_meta = current_user['industryData']['meta']
  3. date1 is combined with shift.startAt and parsed:
    f'{date1}T{shift.startAt}'  →  '%d/%m/%YT%H:%M:%S'  →  date1_local_with_shift
  4. DateTimeUtil().convert_date_from_local_to_utc(date1_local_with_shift) converts to UTC
  5. date1_utc_with_shift = utc_result - timedelta(days=days_offset)
  6. Steps 3–5 are repeated for date2 if provided

The method returns four values: date1_utc_with_shift, date2_utc_with_shift, date1_local_with_shift, date2_local_with_shift.

Example — Industry with timezone=Asia/Kolkata, startAt=06:00:00, daysOffset=0:

FieldValue
Requestdate1=09/11/2024
Parsed local09/11/2024 06:00:00 IST
Converted to UTC09/11/2024 00:30:00 UTC
Cosmos DB query range09/11/2024 00:30:00 UTC → 10/11/2024 00:30:00 UTC
note

Edge case — daysOffset=1: The industry's logical day starts before midnight in local time. A request for date1=09/11/2024 will shift the UTC start back by one day. This is transparently handled; callers always pass the local business date.

Concurrent Data Fetch

Once date conversion completes, the route spawns up to 4 concurrent futures via ThreadPoolExecutor:

FutureMethodConditionResult slot
1get_latest_data()DeviceDataService.get_latest_data(date1_utc_with_shift)Only if includeToday=Truelatest_data (slot c=1)
2get_last_data()DeviceDataService.get_last_updated_time()Alwayslast_updated_time (slot c=2)
3get_devices_data()Alwaysdata1, data2 (slot c=3)
4get_last_seven_days_data()Only if type=='HOUR' AND qualityCategory presentquality_graph1, quality_graph2 (slot c=4)
info

Future 3 — date range selection:

The Cosmos DB query coordinates differ by aggregation type:

typeQuery coordinates
HOURDeviceDataService.get_data(date1_utc, date2_utc) — UTC timestamps
DAY, MONTHDeviceDataService.get_data(date1_local, date2_local) — local timestamps

Future 4 — quality week data:

Runs get_device_data_for_quality_week(date1_utc) and, if date2 was provided, a second call with date2_utc. Only executes when both conditions are met: type=='HOUR' and qualityCategory is present in the request context.

Formatting

The collected results are passed to DeviceDataFormatter.format_data(). The date arguments passed to the formatter differ by type to match the query coordinates used in Future 3:

typeFormatter date arguments
HOURformat_data(data1, data2, latest_data, last_updated_time, date1_utc, date2_utc, date1_utc, date2_utc, quality_graph1, quality_graph2)
DAY, MONTHformat_data(data1, data2, latest_data, last_updated_time, date1_local, date2_local, date1_utc, date2_utc, quality_graph1, quality_graph2)

Edge Cases

warning
ScenarioBehaviour
includeToday=FalseFuture 1 is skipped entirely; latest_data is None; formatter receives None for that slot
qualityCategory absent or type != 'HOUR'Future 4 is skipped; quality_graph1 and quality_graph2 are None
date2 not providedOnly date1 range is queried; data2 and quality_graph2 are None; formatter renders single-date view
divisionFactor not sentDefaults to 1000 before any processing; raw Cosmos readings are in milli-units
daysOffset non-integer in DBint() cast raises ValueError — treat as data integrity issue in industryData

Status Codes

CodeCause
200Data returned successfully
401Missing or invalid JWT, or category not in user's allowed categories
1403date1, type, or category missing

Offline Device Thresholds

Devices are classified as offline based on how long has elapsed since their last data transmission. The threshold varies by the view period being rendered:

View periodMinutes without data before "offline"
Hour view15 minutes
Day view60 minutes
Month view1800 minutes
note

These thresholds are defined in Constants and are applied in both the user device data formatters and the admin offline devices endpoint.


Status Code Reference

CodeMeaning
200Success
401Missing or invalid JWT; category not in user's allowed categories
1401Invalid category value — does not exist in user context
1403Missing required parameter (date1, type, category, or unitId)