Data Models
Comprehensive guide to all data models and their relationships in Gridova
User Model
user.jsonjson
{
"id": "user_123abc",
"email": "owner@example.com",
"name": "John Doe",
"role": "owner", // "owner", "tenant", "admin"
"phone": "+628123456789",
"avatar_url": "https://cdn.gridova.com/avatars/user_123.jpg",
"created_at": "2024-01-15T08:30:00Z",
"updated_at": "2024-06-20T14:22:00Z",
"settings": {
"notifications_enabled": true,
"email_alerts": true,
"language": "id",
"currency": "IDR",
"timezone": "Asia/Jakarta"
}
}Boarding House Model
boarding_house.jsonjson
{
"id": "bh_456def",
"owner_id": "user_123abc",
"name": "Kos Pak Budi",
"description": "Boarding house near campus with 24/7 electricity monitoring",
"address": {
"street": "Jl. Sudirman No. 123",
"city": "Jakarta",
"province": "DKI Jakarta",
"postal_code": "12345",
"country": "Indonesia",
"coordinates": {
"latitude": -6.2088,
"longitude": 106.8456
}
},
"total_rooms": 10,
"occupied_rooms": 7,
"electricity_rate": 1500, // IDR per kWh
"admin_fee": 5000, // IDR per month
"created_at": "2024-01-15T08:30:00Z",
"updated_at": "2024-06-20T14:22:00Z"
}Room Model
room.jsonjson
{
"id": "room_789ghi",
"boarding_house_id": "bh_456def",
"room_number": "A-101",
"floor": 1,
"type": "single", // "single", "double", "suite"
"status": "occupied", // "vacant", "occupied", "maintenance"
"monthly_rent": 1500000, // IDR
"size_sqm": 12.0,
"facilities": [
"AC",
"Private Bathroom",
"WiFi",
"Wardrobe"
],
"device_id": "ESP32_001", // Associated IoT device
"current_tenant_id": "user_tenant_001",
"created_at": "2024-01-15T08:30:00Z",
"updated_at": "2024-06-20T14:22:00Z"
}Tenant Model
tenant.jsonjson
{
"id": "user_tenant_001",
"user_id": "user_999xyz",
"room_id": "room_789ghi",
"boarding_house_id": "bh_456def",
"check_in_date": "2024-06-01",
"check_out_date": null, // null if still active
"contract_duration": 12, // months
"deposit_amount": 1500000, // IDR
"emergency_contact": {
"name": "Jane Doe",
"relationship": "Mother",
"phone": "+628987654321"
},
"id_card_number": "3201234567890123",
"id_card_photo_url": "https://cdn.gridova.com/documents/ktp_001.jpg",
"status": "active", // "active", "expired", "terminated"
"created_at": "2024-06-01T10:00:00Z",
"updated_at": "2024-06-20T14:22:00Z"
}Device Model
device.jsonjson
{
"id": "ESP32_001",
"room_id": "room_789ghi",
"boarding_house_id": "bh_456def",
"name": "Room A-101 Monitor",
"type": "esp32",
"hardware": {
"board": "ESP32-WROOM-32",
"sensors": ["PZEM-004T"],
"relays": 2
},
"firmware_version": "1.2.3",
"status": "online", // "online", "offline", "error"
"ip_address": "192.168.1.100",
"mac_address": "AA:BB:CC:DD:EE:FF",
"signal_strength": -45, // dBm
"uptime": 345600, // seconds
"last_seen": "2024-06-25T10:30:45Z",
"relay_states": {
"relay1": true,
"relay2": false
},
"settings": {
"data_interval": 5000, // ms
"alert_threshold_power": 2000, // watts
"auto_shutoff_enabled": false
},
"created_at": "2024-01-20T09:15:00Z",
"updated_at": "2024-06-25T10:30:45Z"
}Sensor Data Model
sensor_data.jsonjson
{
"id": "sd_001abc",
"device_id": "ESP32_001",
"room_id": "room_789ghi",
"timestamp": "2024-06-25T10:30:45Z",
"data": {
"voltage": 220.5, // Volts
"current": 2.83, // Amperes
"power": 623.81, // Watts
"energy": 12.456, // kWh (cumulative)
"frequency": 50.0, // Hz
"power_factor": 0.99,
"apparent_power": 630.22, // VA
"reactive_power": 89.15 // VAR
},
"calculated": {
"cost_hour": 935.72, // IDR per hour at current power
"cost_day": 22457.28, // IDR per day
"energy_today": 8.234, // kWh today
"cost_today": 12351.00 // IDR today
}
}Data Retention
Raw sensor data is kept for 30 days. Hourly aggregates are kept for 1 year. Daily/monthly summaries are kept indefinitely for billing purposes.
Relay Control Model
relay_control.jsonjson
{
"id": "rc_002xyz",
"device_id": "ESP32_001",
"room_id": "room_789ghi",
"relay_number": 1,
"action": "on", // "on", "off", "toggle"
"triggered_by": "user_123abc",
"trigger_type": "manual", // "manual", "scheduled", "automation", "emergency"
"timestamp": "2024-06-25T10:31:00Z",
"success": true,
"response_time_ms": 234,
"metadata": {
"source": "mobile_app",
"app_version": "1.5.2",
"platform": "android"
}
}Transaction Model
transaction.jsonjson
{
"id": "txn_003abc",
"tenant_id": "user_tenant_001",
"room_id": "room_789ghi",
"boarding_house_id": "bh_456def",
"type": "electricity", // "rent", "electricity", "deposit", "refund"
"period": {
"month": 6,
"year": 2024,
"start_date": "2024-06-01",
"end_date": "2024-06-30"
},
"amount": 185000, // IDR
"breakdown": {
"energy_kwh": 123.45,
"rate_per_kwh": 1500,
"subtotal": 185175,
"admin_fee": 0,
"adjustment": -175,
"total": 185000
},
"status": "paid", // "pending", "paid", "overdue", "cancelled"
"payment_method": "transfer", // "cash", "transfer", "ewallet", "card"
"payment_date": "2024-07-01T08:15:00Z",
"payment_proof_url": "https://cdn.gridova.com/payments/proof_003.jpg",
"due_date": "2024-07-05",
"notes": "June 2024 electricity bill",
"created_at": "2024-07-01T00:00:00Z",
"updated_at": "2024-07-01T08:15:00Z"
}Alert Model
alert.jsonjson
{
"id": "alert_004xyz",
"device_id": "ESP32_001",
"room_id": "room_789ghi",
"type": "high_power", // "high_power", "offline", "sensor_error", "overload"
"severity": "warning", // "info", "warning", "error", "critical"
"title": "High Power Consumption",
"message": "Power usage exceeded 2000W threshold",
"data": {
"current_power": 2350.5,
"threshold": 2000.0,
"duration_seconds": 120
},
"resolved": false,
"resolved_at": null,
"resolved_by": null,
"timestamp": "2024-06-25T10:32:00Z",
"notifications_sent": {
"push": true,
"email": true,
"sms": false
}
}Automation Rule Model
automation_rule.jsonjson
{
"id": "rule_005abc",
"user_id": "user_123abc",
"room_id": "room_789ghi",
"device_id": "ESP32_001",
"name": "Auto shutoff at night",
"enabled": true,
"trigger": {
"type": "schedule", // "schedule", "sensor", "manual"
"schedule": {
"time": "23:00",
"days": ["mon", "tue", "wed", "thu", "fri", "sat", "sun"],
"timezone": "Asia/Jakarta"
}
},
"conditions": [
{
"type": "power",
"operator": "<",
"value": 50,
"unit": "watts"
}
],
"actions": [
{
"type": "relay_control",
"relay": 1,
"state": false
},
{
"type": "notification",
"message": "Room A-101 power automatically turned off"
}
],
"last_triggered": "2024-06-24T23:00:00Z",
"trigger_count": 45,
"created_at": "2024-02-10T14:30:00Z",
"updated_at": "2024-06-20T09:15:00Z"
}Energy Summary Model
energy_summary.jsonjson
{
"id": "summary_006xyz",
"room_id": "room_789ghi",
"period_type": "monthly", // "daily", "weekly", "monthly", "yearly"
"period": {
"month": 6,
"year": 2024
},
"statistics": {
"total_energy_kwh": 123.45,
"total_cost": 185175,
"average_power": 175.8,
"peak_power": 2350.5,
"peak_timestamp": "2024-06-15T14:30:00Z",
"minimum_power": 12.3,
"hours_active": 720,
"hours_off": 0
},
"comparison": {
"previous_period_kwh": 118.32,
"change_percentage": 4.33,
"trend": "increasing"
},
"daily_breakdown": [
{
"date": "2024-06-01",
"energy_kwh": 4.12,
"cost": 6180,
"avg_power": 171.67
},
// ... other days
],
"cost_breakdown": {
"energy_cost": 185175,
"admin_fee": 5000,
"tax": 0,
"total": 190175
},
"generated_at": "2024-07-01T00:05:00Z"
}Model Relationships
API Response Format
All API responses follow this standard format:
Success Response
success_response.jsonjson
{
"success": true,
"data": {
// Requested data here
},
"meta": {
"timestamp": "2024-06-25T10:30:45Z",
"request_id": "req_abc123"
}
}Success with Pagination
paginated_response.jsonjson
{
"success": true,
"data": [
// Array of items
],
"pagination": {
"page": 1,
"per_page": 20,
"total_items": 157,
"total_pages": 8,
"has_next": true,
"has_prev": false
},
"meta": {
"timestamp": "2024-06-25T10:30:45Z",
"request_id": "req_abc123"
}
}Error Response
error_response.jsonjson
{
"success": false,
"error": {
"code": "DEVICE_NOT_FOUND",
"message": "Device with ID 'ESP32_999' not found",
"details": {
"device_id": "ESP32_999"
}
},
"meta": {
"timestamp": "2024-06-25T10:30:45Z",
"request_id": "req_abc123"
}
}Common Error Codes
| Code | HTTP Status | Description |
|---|---|---|
UNAUTHORIZED | 401 | Invalid or missing authentication token |
FORBIDDEN | 403 | User lacks permission for this resource |
NOT_FOUND | 404 | Requested resource does not exist |
VALIDATION_ERROR | 422 | Invalid request data or parameters |
RATE_LIMIT_EXCEEDED | 429 | Too many requests in given timeframe |
DEVICE_OFFLINE | 503 | Device is not connected or responding |
SERVER_ERROR | 500 | Internal server error occurred |
TypeScript Type Definitions
types.tstypescript
// User types
export interface User {
id: string;
email: string;
name: string;
role: 'owner' | 'tenant' | 'admin';
phone?: string;
avatar_url?: string;
created_at: string;
updated_at: string;
settings: UserSettings;
}
export interface UserSettings {
notifications_enabled: boolean;
email_alerts: boolean;
language: string;
currency: string;
timezone: string;
}
// Boarding House types
export interface BoardingHouse {
id: string;
owner_id: string;
name: string;
description?: string;
address: Address;
total_rooms: number;
occupied_rooms: number;
electricity_rate: number;
admin_fee: number;
created_at: string;
updated_at: string;
}
export interface Address {
street: string;
city: string;
province: string;
postal_code: string;
country: string;
coordinates?: Coordinates;
}
export interface Coordinates {
latitude: number;
longitude: number;
}
// Room types
export interface Room {
id: string;
boarding_house_id: string;
room_number: string;
floor: number;
type: 'single' | 'double' | 'suite';
status: 'vacant' | 'occupied' | 'maintenance';
monthly_rent: number;
size_sqm: number;
facilities: string[];
device_id?: string;
current_tenant_id?: string;
created_at: string;
updated_at: string;
}
// Device types
export interface Device {
id: string;
room_id: string;
boarding_house_id: string;
name: string;
type: string;
hardware: DeviceHardware;
firmware_version: string;
status: 'online' | 'offline' | 'error';
ip_address?: string;
mac_address: string;
signal_strength?: number;
uptime: number;
last_seen: string;
relay_states: RelayStates;
settings: DeviceSettings;
created_at: string;
updated_at: string;
}
export interface DeviceHardware {
board: string;
sensors: string[];
relays: number;
}
export interface RelayStates {
relay1: boolean;
relay2: boolean;
}
export interface DeviceSettings {
data_interval: number;
alert_threshold_power: number;
auto_shutoff_enabled: boolean;
}
// Sensor Data types
export interface SensorData {
id: string;
device_id: string;
room_id: string;
timestamp: string;
data: SensorReadings;
calculated: CalculatedData;
}
export interface SensorReadings {
voltage: number;
current: number;
power: number;
energy: number;
frequency: number;
power_factor: number;
apparent_power?: number;
reactive_power?: number;
}
export interface CalculatedData {
cost_hour: number;
cost_day: number;
energy_today: number;
cost_today: number;
}
// Transaction types
export interface Transaction {
id: string;
tenant_id: string;
room_id: string;
boarding_house_id: string;
type: 'rent' | 'electricity' | 'deposit' | 'refund';
period: TransactionPeriod;
amount: number;
breakdown: TransactionBreakdown;
status: 'pending' | 'paid' | 'overdue' | 'cancelled';
payment_method?: string;
payment_date?: string;
payment_proof_url?: string;
due_date: string;
notes?: string;
created_at: string;
updated_at: string;
}
export interface TransactionPeriod {
month: number;
year: number;
start_date: string;
end_date: string;
}
export interface TransactionBreakdown {
energy_kwh?: number;
rate_per_kwh?: number;
subtotal: number;
admin_fee: number;
adjustment: number;
total: number;
}
// Alert types
export interface Alert {
id: string;
device_id: string;
room_id: string;
type: string;
severity: 'info' | 'warning' | 'error' | 'critical';
title: string;
message: string;
data: Record<string, any>;
resolved: boolean;
resolved_at?: string;
resolved_by?: string;
timestamp: string;
notifications_sent: NotificationStatus;
}
export interface NotificationStatus {
push: boolean;
email: boolean;
sms: boolean;
}
// API Response types
export interface ApiResponse<T> {
success: boolean;
data?: T;
error?: ApiError;
pagination?: Pagination;
meta: ApiMeta;
}
export interface ApiError {
code: string;
message: string;
details?: Record<string, any>;
}
export interface Pagination {
page: number;
per_page: number;
total_items: number;
total_pages: number;
has_next: boolean;
has_prev: boolean;
}
export interface ApiMeta {
timestamp: string;
request_id: string;
}