diff --git a/app/Http/Controllers/APIS/CustomerApi/TelemetryController.php b/app/Http/Controllers/APIS/CustomerApi/TelemetryController.php index 479fc1d..44bd8fa 100644 --- a/app/Http/Controllers/APIS/CustomerApi/TelemetryController.php +++ b/app/Http/Controllers/APIS/CustomerApi/TelemetryController.php @@ -6,6 +6,7 @@ use App\Http\Controllers\Controller; use App\Models\Asset; use App\Models\Customer; use App\Models\Device; +use App\Models\DeviceProfileMaster; use App\Models\TimeseriesAlertMessage; use App\Models\TimeseriesKeyMaster; use App\Models\User; @@ -13,12 +14,14 @@ use App\Models\UserAssetLink; use App\Services\AdminService; use App\Services\AlarmService; use App\Services\CustomerInfoService; +use Exception; use Illuminate\Container\Attributes\DB; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB as FacadesDB; use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Validator; +use Carbon\Carbon; class TelemetryController extends Controller { @@ -33,7 +36,6 @@ class TelemetryController extends Controller $this->customerInfoService = $customerInfoService; $this->alarmService = $alarmService; $this->adminService = $adminService; - } @@ -837,4 +839,501 @@ class TelemetryController extends Controller // return response()->json(['telemetry' => $response]); // } + public function convertToUserTimezone($utcDatetime, $userTimezone = 'Asia/Kolkata', $format = 'd-M-Y H:i:s') + { + return Carbon::parse($utcDatetime, 'UTC') + ->setTimezone($userTimezone) + ->format($format); + } + + public function entityQuery(){ + try { + $token = readHeaderToken(); + + // Retrieve devices of user + $deviceIds = UserAssetLink::with('asset.devices') + ->where('user_id', $token['sub']) + ->get() + ->pluck('asset.devices') + ->flatten() + ->pluck('id') + ->toArray(); + + // Retrieve required timeseries of devices + $timeseriesKeys = TimeseriesKeyMaster::where('display_on_dashboard', 1) + ->orWhere('display_on_popup',1) + ->get() + ->pluck('key_name') + ->flatten() + ->toArray(); + + $entityQuery = $this->customerInfoService->fetchEntityQuery($deviceIds, $timeseriesKeys); + + // Ensure the response is converted to an array if it's a JsonResponse + $data = $entityQuery instanceof \Illuminate\Http\JsonResponse ? $entityQuery->getData(true) : $entityQuery; + + $timeseriesKeyMap = TimeseriesKeyMaster::where('display_on_dashboard', 1) + ->orWhere('display_on_popup', 1) + ->get() + ->groupBy('device_profile_xid') + ->map(function ($group) { + return $group->pluck('key_name')->toArray(); + }); + + // Process the array and format the output + $formatted = [ + 'success' => true, + 'data' => collect($data['data'])->map(function ($device) use ($timeseriesKeyMap) { + $deviceType = $device['latest']['ENTITY_FIELD']['type']['value'] ?? null; + $deviceProfileId = DeviceProfileMaster::where('name',$deviceType)->select('id')->first(); + $allTimeseries = $device['latest']['TIME_SERIES'] ?? []; + + // Step 2: Get allowed keys for this device type + $allowedKeys = $timeseriesKeyMap[$deviceProfileId['id']] ?? []; + + // Step 3: Filter timeseries to only include allowed keys + $filteredTimeseries = collect($allTimeseries) + ->only($allowedKeys) + ->toArray(); + + return [ + 'deviceId' => $device['entityId']['id'] ?? null, + 'deviceName' => $device['latest']['ENTITY_FIELD']['name']['value'] ?? null, + 'deviceProfileId' => $deviceProfileId['id'] ?? null, + 'deviceType' => $deviceType, + 'timeseries' => $filteredTimeseries, + ]; + })->toArray() + ]; + + return response()->json($formatted); + + } catch(Exception $e){ + return response()->json(['success' => false, 'message' => $e->getMessage()], 500); + } + } + + public function getDeviceHealth(array $values): string + { + $hasRed = false; + $hasOrange = false; + + foreach ($values as $value) { + if($value['key_name'] == 'PowerLoss_value'){ + if ($value['value'] > 5 && $value['value'] < 10) { + $hasRed = true; + break; // Red is highest priority, no need to continue + } elseif ($value['value'] > 2.5 && $value['value'] < 5) { + $hasOrange = true; + } + } else { + if ($value['value'] > 0 && $value['value'] < 31) { + $hasRed = true; + break; // Red is highest priority, no need to continue + } elseif ($value['value'] < 71) { + $hasOrange = true; + } + } + + } + + if ($hasRed) { + return 'red'; + } elseif ($hasOrange) { + return 'orange'; + } + + return 'green'; + } + + public function getAssetHealth($deviceHealthStatuses) { + if (in_array('red', $deviceHealthStatuses)) { + return 'red'; + } elseif (in_array('orange', $deviceHealthStatuses)) { + return 'orange'; + } + return 'green'; + } + + public function userAssetsNew(){ + try { + $token = readHeaderToken(); + + // Retrieve devices of user + $assetDeviceListing = UserAssetLink::with('asset.devices') + ->where('user_id', $token['sub']) + ->get(); + + $formattedData = $assetDeviceListing->map(function ($link) { + $asset = $link->asset; + $deviceHealthStatuses = []; + + $devicesData = $asset->devices->map(function ($device) use (&$deviceHealthStatuses) { + + $timeseriesKeys = TimeseriesKeyMaster::where('display_on_health_condition', 1) + ->where('device_profile_xid', $device->device_profile_id) + ->pluck('key_name') + ->implode(','); + + // \Log::info("Keys for device {$device->name}", [$timeseriesKeys]); + + $telemetryValue = $this->customerInfoService->fetchTelemetryData($device->id, $timeseriesKeys); + $data = $telemetryValue instanceof \Illuminate\Http\JsonResponse ? $telemetryValue->getData(true) : $telemetryValue; + + // \Log::info("Telemetry data for device {$device->name}", $data); + + $transformedTelemetry = []; + // dd($data); + if (!empty($data) && is_array($data)) { + foreach ($data as $key => $items) { + + foreach ($items as $item) { + $transformedTelemetry[] = [ + 'key_name' => $key, + 'value' => $item['value'] + ]; + } + } + } + // \Log::info("Transformed data for device {$device->name}", $transformedTelemetry); + $deviceHealth = $this->getDeviceHealth($transformedTelemetry); + $deviceHealthStatuses[] = $deviceHealth; + + return [ + 'deviceName' => $device->name, + 'deviceHealth' => $deviceHealth + ]; + })->values(); + + return [ + 'assetName' => $asset->name, + 'assetHealth' => $this->getAssetHealth($deviceHealthStatuses), + 'devices' => $devicesData + ]; + + }); + + return response()->json(['success' => true,'data' => $formattedData]); + + } catch(Exception $e){ + return response()->json(['success' => false, 'message' => $e->getMessage()], 500); + } + } + public function customerDeviceInfoNew(){ + try { + $token = readHeaderToken(); + + // Fetch all devices linked to user in one go + $devices = UserAssetLink::with('asset.devices') + ->where('user_id', $token['sub']) + ->get() + ->pluck('asset.devices') + ->flatten() + ->unique('id'); // Ensure no duplicate devices + + $good = $moderate = $bad = 0; + $deviceIds = $devices->pluck('id')->toArray(); + + foreach ($deviceIds as $deviceId) { + $device = Device::find($deviceId); // get the device row first + + if ($device) { + $timeseriesKeys = TimeseriesKeyMaster::where('display_on_health_condition', 1) + ->where('device_profile_xid', $device->device_profile_id) + ->pluck('key_name') + ->implode(','); + + $telemetryValue = $this->customerInfoService->fetchTelemetryData($device->id, $timeseriesKeys); + $data = $telemetryValue instanceof \Illuminate\Http\JsonResponse ? $telemetryValue->getData(true) : $telemetryValue; + + $transformedTelemetry = []; + + if (!empty($data) && is_array($data)) { + foreach ($data as $key => $items) { + + foreach ($items as $item) { + $transformedTelemetry[] = [ + 'key_name' => $key, + 'value' => $item['value'] + ]; + } + } + } + // \Log::info("Transformed data for device {$device->name}", $transformedTelemetry); + $deviceHealth = $this->getDeviceHealth($transformedTelemetry); + + match ($deviceHealth) { + 'green' => $good++, + 'orange' => $moderate++, + default => $bad++ + }; + } + } + + $deviceCount = $this->customerInfoService->getDevicesCount($deviceIds); + $alarms = $this->customerInfoService->fetchDeviceAlarms($deviceIds); + + $response = [ + 'success' => true, + 'good' => $good, + 'moderate' => $moderate, + 'bad' => $bad, + 'total' => $deviceCount['totalDevices'], + 'active' => $deviceCount['activeDevices'], + 'alarm' => $alarms['count'] + ]; + + return response()->json($response); + + } catch(Exception $e){ + return response()->json(['success' => false, 'message' => $e->getMessage()], 500); + } + } + + public function getDeviceIndicators($assetId){ + try{ + $token = readHeaderToken(); + + $assetDeviceListing = UserAssetLink::with('asset.devices') + ->where(['user_id' => $token['sub'], 'asset_id' => $assetId]) + ->get(); + + $assetLink = $assetDeviceListing->map(function($link){ + $asset = $link->asset; + $deviceData = $asset->devices->map(function($device){ + + $deviceProfile = Device::with('deviceProfile') + ->where('id', $device->id) + ->first(); + + $deviceProfileId = $deviceProfile?->deviceProfile?->id; + $deviceProfileName = $deviceProfile?->deviceProfile?->name; + + $timeseriesList = TimeseriesKeyMaster::where('display_on_dashboard', 1) + ->where('device_profile_xid', $deviceProfileId) + ->get(['key_name', 'display_name']); + + $keyNameList = $timeseriesList->pluck('key_name')->implode(','); + + $displayNameMap = $timeseriesList->pluck('display_name', 'key_name')->toArray(); + + $telemetryValue = $this->customerInfoService->fetchTelemetryData($device->id, $keyNameList); + $data = $telemetryValue instanceof \Illuminate\Http\JsonResponse ? $telemetryValue->getData(true) : $telemetryValue; + + $transformedTelemetry = []; + + if (!empty($data) && is_array($data)) { + foreach ($data as $key => $items) { + + foreach ($items as $item) { + $transformedTelemetry[] = [ + 'display_name' => $displayNameMap[$key] ?? $key, + 'value' => $item['value'] + ]; + } + } + } + + return [ + 'deviceId' => $device->id, + 'deviceName' => $device->name, + 'deviceType' => $deviceProfileName, + 'indicator' => $transformedTelemetry + ]; + })->values(); + + return [ + 'data' => $deviceData + ]; + + }); + + return response()->json($assetLink); + + } catch(Exception $e){ + return response()->json(['success' => false, 'message' => $e->getMessage()], 500); + } + } + + public function getAlerts($deviceId){ + try{ + $token = readHeaderToken(); + + $deviceParams = TimeseriesKeyMaster::select('key_name', 'display_name') + ->where('display_on_alerts', 1) + ->whereHas('device', function ($query) use ($deviceId) { + $query->where('id', $deviceId); + }) + ->get(); + + $keyNameList = $deviceParams->pluck('key_name')->implode(','); + $displayNameMap = $deviceParams->pluck('display_name', 'key_name')->toArray(); + + $telemetryValue = $this->customerInfoService->fetchTelemetryData($deviceId, $keyNameList); + $data = $telemetryValue instanceof \Illuminate\Http\JsonResponse ? $telemetryValue->getData(true) : $telemetryValue; + + $transformedTelemetry = []; + + if (!empty($data) && is_array($data)) { + foreach ($data as $key => $items) { + + foreach ($items as $item) { + + $timeseriesId = TimeseriesKeyMaster::where('display_name', $displayNameMap[$key])->first('id'); + + $fetchMsg = TimeseriesAlertMessage::where('timeseries_key_master_xid', $timeseriesId['id']) + ->where('min_value', '<=', $item['value']) + ->where('max_value', '>=', $item['value']) + ->first(['alert_msg']); + + $alertPoints = []; + + if (!empty($fetchMsg?->alert_msg)) { + $alertPoints = explode("\n", trim($fetchMsg->alert_msg)); + } + + $dateTime = Carbon::createFromTimestamp($item['ts'] / 1000)->format('d-M-Y H:i:s'); + + $transformedTelemetry[] = [ + 'display_name' => $displayNameMap[$key] ?? $key, + 'alert_msg' => $alertPoints ?: ["No Alerts & Instructions"] + ]; + } + } + } + + return response()->json( + [ + 'updated_time' => $this->convertToUserTimezone($dateTime), + 'data' => $transformedTelemetry + ] + ); + + } catch(Exception $e){ + return response()->json(['success' => false, 'message' => $e->getMessage()], 500); + } + } + + public function getGlobalIndicators($deviceId){ + try{ + $token = readHeaderToken(); + + $deviceParams = TimeseriesKeyMaster::select('key_name', 'display_name') + ->where('display_on_popup', 1) + ->whereHas('device', function ($query) use ($deviceId) { + $query->where('id', $deviceId); + }) + ->get(); + + $keyNameList = $deviceParams->pluck('key_name')->implode(','); + $displayNameMap = $deviceParams->pluck('display_name', 'key_name')->toArray(); + + $telemetryValue = $this->customerInfoService->fetchTelemetryData($deviceId, $keyNameList); + $data = $telemetryValue instanceof \Illuminate\Http\JsonResponse ? $telemetryValue->getData(true) : $telemetryValue; + + $transformedTelemetry = []; + + if (!empty($data) && is_array($data)) { + foreach ($data as $key => $items) { + + foreach ($items as $item) { + + $dateTime = Carbon::createFromTimestamp($item['ts'] / 1000)->format('d-M-Y H:i:s'); + + switch (true) { + case $item['value'] > 70: + $status = 'Stable'; + break; + + case $item['value'] > 30: + $status = 'Attention'; + break; + + default: + $status = 'Alert'; + } + + $transformedTelemetry[] = [ + 'display_name' => $displayNameMap[$key] ?? $key, + 'value' => $item['value'], + 'health_status' => $status + ]; + } + } + } + + return response()->json( + [ + 'updated_time' => $this->convertToUserTimezone($dateTime), + 'data' => $transformedTelemetry + ] + ); + + } catch(Exception $e){ + return response()->json(['success' => false, 'message' => $e->getMessage()], 500); + } + } + + public function getTrends($deviceId){ + try { + + $token = readHeaderToken(); + + $dates = collect(range(0, 6))->map(function ($i) { + return Carbon::now()->subDays($i)->format('M d, Y'); + })->reverse()->values()->toArray(); + + foreach($dates as $date){ + $dateTs[] = (Carbon::createFromFormat('M d, Y', $date)->timestamp)*1000; + // Start timestamp (beginning of the day) + $ts['startTs'] = Carbon::createFromFormat('M d, Y', $date)->startOfDay()->timestamp * 1000; + + // End timestamp (end of the day) + $ts['endTs'] = Carbon::createFromFormat('M d, Y', $date)->endOfDay()->timestamp * 1000; + + $deviceProfile = Device::with('deviceProfile') + ->where('id', $deviceId) + ->first(); + + $deviceProfileId = $deviceProfile?->deviceProfile?->id; + + $parameters = parameters(); + $value1 = $value2 = []; + + $telemetryValue1 = $this->customerInfoService->fetchTelemetryData($deviceId, $parameters[$deviceProfileId]['keyNameList1'], $ts); + $trendsData1 = $telemetryValue1 instanceof \Illuminate\Http\JsonResponse ? $telemetryValue1->getData(true) : $telemetryValue1; + + $telemetryValue2 = $this->customerInfoService->fetchTelemetryData($deviceId, $parameters[$deviceProfileId]['keyNameList2'], $ts); + $trendsData2 = $telemetryValue2 instanceof \Illuminate\Http\JsonResponse ? $telemetryValue2->getData(true) : $telemetryValue2; + + foreach ($trendsData1 as $key => $trend) { + $displayName = TimeseriesKeyMaster::where('key_name',$key)->first('display_name'); + foreach ($trend as $val) { + $value1[0][$displayName['display_name']][] = $val['value']; + } + } + + foreach ($trendsData2 as $key => $trend) { + $displayName = TimeseriesKeyMaster::where('key_name',$key)->first('display_name'); + foreach ($trend as $val) { + $value2[0][$displayName['display_name']][] = $val['value']; + } + } + + $graph1[$date] = $value1; + $graph2[$date] = $value2; + + } + + return [ + 'success' => true, + 'graph1' => $graph1, + 'graph2' => $graph2, + ]; + + } catch(Exception $e){ + return response()->json(['success' => false, 'message' => $e->getMessage()], 500); + } + } + } diff --git a/app/Http/Helpers/TrendKeysHelper.php b/app/Http/Helpers/TrendKeysHelper.php new file mode 100644 index 0000000..da31141 --- /dev/null +++ b/app/Http/Helpers/TrendKeysHelper.php @@ -0,0 +1,43 @@ + [ + "keyNameList1" => 'PowerLoss_value,ChannelSpeed', + "keyNameList2" => 'MechanicalHealth_valueInHealth,ChannelSpeed', + ], + "88986090-16b3-11f0-a9dc-45dd276e4cd5" => [ + "keyNameList1" => 'PowerLoss_value,ChannelSpeed', + "keyNameList2" => 'MechanicalHealth_valueInHealth,ChannelSpeed', + ], + "a7802800-f34d-11ef-a9dc-45dd276e4cd5" => [ + "keyNameList1" => 'PowerLoss_value,ChannelSpeed', + "keyNameList2" => 'MechanicalHealth_valueInHealth,ChannelSpeed', + ], + "b82d42a0-f34d-11ef-a9dc-45dd276e4cd5" => [ + "keyNameList1" => 'GlobalMixed_valueInPercent,ChannelSpeed', + "keyNameList2" => 'BearingGlobal_valueInPercent,ChannelSpeed', + ], + "b60d08f0-16b3-11f0-a9dc-45dd276e4cd5" => [ + "keyNameList1" => 'GlobalMixed_valueInPercent,ChannelSpeed', + "keyNameList2" => 'BearingGlobal_valueInPercent,ChannelSpeed', + ], + "b13497a0-f34d-11ef-a9dc-45dd276e4cd5" => [ + "keyNameList1" => 'RegularityDeviation_valueInPercent,BladeStatus_valueInPercent,ChannelSpeed', + "keyNameList2" => 'BearingStatus_valueInPercent,TurbineCoupling_valueInPercent,ChannelSpeed', + ], + "72907b10-04ad-11f0-a9dc-45dd276e4cd5" => [ + "keyNameList1" => 'RegularityDeviation_valueInPercent,BladeStatus_valueInPercent,ChannelSpeed', + "keyNameList2" => 'BearingStatus_valueInPercent,TurbineCoupling_valueInPercent,ChannelSpeed', + ], + "bfbd2490-f34d-11ef-a9dc-45dd276e4cd5" => [ + "keyNameList1" => 'StaticTorque_value,ChannelSpeed', + "keyNameList2" => 'StaticPower_value,ChannelSpeed', + ] + ]; + + return $keys; + +} diff --git a/app/Http/Helpers/Webhelper.php b/app/Http/Helpers/Webhelper.php index be9ee5f..85328ac 100644 --- a/app/Http/Helpers/Webhelper.php +++ b/app/Http/Helpers/Webhelper.php @@ -369,4 +369,48 @@ if (!function_exists('getTokenFromHeader')) { } } } -} \ No newline at end of file +} + +if (!function_exists('parameters')) { +function parameters() +{ + + $keys = [ + "4e989080-04ad-11f0-a9dc-45dd276e4cd5" => [ + "keyNameList1" => 'PowerLoss_value,ChannelSpeed', + "keyNameList2" => 'MechanicalHealth_valueInHealth,ChannelSpeed', + ], + "88986090-16b3-11f0-a9dc-45dd276e4cd5" => [ + "keyNameList1" => 'PowerLoss_value,ChannelSpeed', + "keyNameList2" => 'MechanicalHealth_valueInHealth,ChannelSpeed', + ], + "a7802800-f34d-11ef-a9dc-45dd276e4cd5" => [ + "keyNameList1" => 'PowerLoss_value,ChannelSpeed', + "keyNameList2" => 'MechanicalHealth_valueInHealth,ChannelSpeed', + ], + "b82d42a0-f34d-11ef-a9dc-45dd276e4cd5" => [ + "keyNameList1" => 'GlobalMixed_valueInPercent,ChannelSpeed', + "keyNameList2" => 'BearingGlobal_valueInPercent,ChannelSpeed', + ], + "b60d08f0-16b3-11f0-a9dc-45dd276e4cd5" => [ + "keyNameList1" => 'GlobalMixed_valueInPercent,ChannelSpeed', + "keyNameList2" => 'BearingGlobal_valueInPercent,ChannelSpeed', + ], + "b13497a0-f34d-11ef-a9dc-45dd276e4cd5" => [ + "keyNameList1" => 'RegularityDeviation_valueInPercent,BladeStatus_valueInPercent,ChannelSpeed', + "keyNameList2" => 'BearingStatus_valueInPercent,TurbineCoupling_valueInPercent,ChannelSpeed', + ], + "72907b10-04ad-11f0-a9dc-45dd276e4cd5" => [ + "keyNameList1" => 'RegularityDeviation_valueInPercent,BladeStatus_valueInPercent,ChannelSpeed', + "keyNameList2" => 'BearingStatus_valueInPercent,TurbineCoupling_valueInPercent,ChannelSpeed', + ], + "bfbd2490-f34d-11ef-a9dc-45dd276e4cd5" => [ + "keyNameList1" => 'StaticTorque_value,ChannelSpeed', + "keyNameList2" => 'StaticPower_value,ChannelSpeed', + ] + ]; + + return $keys; + +} +} diff --git a/app/Models/Asset.php b/app/Models/Asset.php index fd8dc5f..df9966c 100644 --- a/app/Models/Asset.php +++ b/app/Models/Asset.php @@ -53,6 +53,11 @@ class Asset extends Model return $this->belongsTo(Customer::class, 'customer_xid', 'id'); } + public function userLinks() + { + return $this->hasMany(UserAssetLink::class, 'asset_id', 'id'); + } + public function devices() { return $this->hasMany(Device::class, 'asset_id', 'id'); diff --git a/app/Services/AdminService.php b/app/Services/AdminService.php index 7e6915d..3526bb3 100644 --- a/app/Services/AdminService.php +++ b/app/Services/AdminService.php @@ -33,12 +33,11 @@ class AdminService // 'accept' => 'application/json', 'Content-Type' => 'application/json', ]) - ->post("{$this->baseUrl}/api/auth/login", [ + ->post("{$this->baseUrl}api/auth/login", [ 'username' => $this->username, 'password' => $this->password, ]); - if ($response->successful()) { $token = $response->json('token'); Cache::put('thingsboard_token', $token, now()->addMinutes(15)); diff --git a/app/Services/CustomerInfoService.php b/app/Services/CustomerInfoService.php index cbdb6f0..27e7df5 100644 --- a/app/Services/CustomerInfoService.php +++ b/app/Services/CustomerInfoService.php @@ -364,7 +364,6 @@ class CustomerInfoService $username = env('THINGSBOARD_USERNAME', 'tenant1@thingsboard.org'); $password = env('THINGSBOARD_PASSWORD', 'tenant1'); - if (Cache::has('thingsboard_token')) { return Cache::get('thingsboard_token'); } @@ -381,7 +380,7 @@ class CustomerInfoService if ($response->successful()) { $token = $response->json('token'); - Cache::put('thingsboard_token', $token, now()->addMinutes(15)); + Cache::put('thingsboard_token', $token, now()->addMinutes(1440)); return $token; } else { Log::error("ThingsBoard Authentication Failed: " . $response->body()); @@ -389,4 +388,222 @@ class CustomerInfoService } } + public function fetchEntityQuery($deviceIds, $timeseriesKeys){ + try{ + $token = $this->adminService->getToken(); + if (!$token) { + throw new \Exception("Failed to authenticate with ThingsBoard"); + } + + $url = env('THINGSBOARD_URL').'api/entitiesQuery/find'; + + $timeSeries = array_map(function($key) { + return [ + 'type' => 'TIME_SERIES', + 'key' => $key + ]; + }, $timeseriesKeys); + + $payload = [ + "entityFilter" => [ + "type" => "entityList", + "entityType" => "DEVICE", + "entityList" => $deviceIds + ], + "pageLink" => [ + "pageSize" => 100, + "page" => 0, + "textSearch" => "", + "dynamic" => true + ], + "entityFields" => [ + ["type" => "ENTITY_FIELD", "key" => "name"], + ["type" => "ENTITY_FIELD", "key" => "type"], + ], + "latestValues" => $timeSeries + ]; + + $response = Http::withHeaders([ + 'accept' => 'application/json', + 'Content-Type' => 'application/json', + 'X-Authorization' => 'Bearer ' . $token, + ])->post($url, $payload); + + // Return JSON response + return response()->json($response->json(), $response->status()); + + } catch(Exception $e){ + return response()->json(['success' => false, 'message' => $e->getMessage()], 500); + } + } + + public function fetchTelemetryData($deviceId,$keys,$ts=[]) + { + $url = env('THINGSBOARD_URL') ."api/plugins/telemetry/DEVICE/{$deviceId}/values/timeseries?useStrictDataTypes=false"; + + $token = $this->adminService->getToken(); + if (!$token) { + throw new \Exception("Failed to authenticate with ThingsBoard"); + } + + try { + $response = Http::withToken($token)->asForm()->get($url, [ + 'keys' => $keys, + 'startTs' => $ts['startTs'] ?? null, + 'endTs' => $ts['endTs'] ?? null, + 'limit' => $ts ? 25000 : null + ]); + + if ($response->successful()) { + return $response->json(); + } else { + return response()->json([ + 'success' => false, + 'message' => 'Failed to fetch data', + 'status' => $response->status(), + ], $response->status()); + } + } catch (\Exception $e) { + return response()->json([ + 'success' => false, + 'message' => 'An error occurred: ' . $e->getMessage(), + ], 500); + } + } + + public function getDevicesCount($deviceIds){ + try { + $token = $this->adminService->getToken(); + if (!$token) { + throw new \Exception("Failed to authenticate with ThingsBoard"); + } + + $url = env('THINGSBOARD_URL') . 'api/entitiesQuery/count'; + + // ---------- 1. Total count ---------- + $totalPayload = [ + "entityFilter" => [ + "type" => "entityList", + "entityType" => "DEVICE", + "entityList" => $deviceIds + ] + ]; + + $totalResponse = Http::withHeaders([ + 'accept' => 'application/json', + 'Content-Type' => 'application/json', + 'X-Authorization' => 'Bearer ' . $token, + ])->post($url, $totalPayload); + + $totalCount = $totalResponse->json() ?? 0; + + // ---------- 2. active count (active = false) ---------- + $activePayload = [ + "entityFilter" => [ + "type" => "entityList", + "entityType" => "DEVICE", + "entityList" => $deviceIds + ], + "keyFilters" => [ + [ + "key" => [ + "type" => "ATTRIBUTE", + "key" => "active" + ], + "valueType" => "BOOLEAN", + "predicate" => [ + "operation" => "EQUAL", + "value" => [ + "defaultValue" => true, + "dynamicValue" => null + ], + "type" => "BOOLEAN" + ] + ] + ] + ]; + + $activeResponse = Http::withHeaders([ + 'accept' => 'application/json', + 'Content-Type' => 'application/json', + 'X-Authorization' => 'Bearer ' . $token, + ])->post($url, $activePayload); + + $activeCount = $activeResponse->json() ?? 0; + + // ---------- Return both counts ---------- + return [ + 'totalDevices' => $totalCount, + 'activeDevices' => $activeCount + ]; + + + } catch(Exception $e){ + return response()->json(['success' => false, 'message' => $e->getMessage()], 500); + } + } + +public function fetchDeviceAlarms(array $deviceIds) + { + try { + $token = $this->adminService->getToken(); + if (!$token) { + throw new \Exception("Failed to authenticate with ThingsBoard"); + } + + $url = env('THINGSBOARD_URL') . 'api/alarmsQuery/find'; + + // Timestamp range (modify as needed) + $startTs = now()->subDays(30)->timestamp * 1000; + $endTs = now()->timestamp * 1000; + + $payload = [ + "entityFilter" => [ + "type" => "entityList", + "entityType" => "DEVICE", + "entityList" => $deviceIds + ], + "pageLink" => [ + "startTs" => $startTs, + "endTs" => $endTs, + "pageSize" => 100, + "page" => 0, + "textSearch" => "", + "sortOrder" => [ + "key" => [ + "type" => "ALARM_FIELD", + "key" => "createdTime" + ], + "direction" => "DESC" + ] + ], + "alarmFields" => [] + ]; + + $response = Http::withHeaders([ + 'accept' => 'application/json', + 'Content-Type' => 'application/json', + 'X-Authorization' => 'Bearer ' . $token, + ])->post($url, $payload); + + if (!$response->successful()) { + throw new \Exception("Failed to fetch alarms: " . $response->body()); + } + + $alarmData = $response->json(); + + return [ + 'data' => $alarmData['data'] ?? [], + 'count' => $alarmData['totalElements'] ?? 0 + ]; + + } catch (\Exception $e) { + return response()->json([ + 'success' => false, + 'message' => 'Error: ' . $e->getMessage() + ], 500); + } + } + + } diff --git a/config/auth.php b/config/auth.php index bd880b4..f5adbf0 100644 --- a/config/auth.php +++ b/config/auth.php @@ -98,7 +98,7 @@ return [ 'users' => [ 'provider' => 'users', 'table' => env('AUTH_PASSWORD_RESET_TOKEN_TABLE', 'password_reset_tokens'), - 'expire' => 60, + 'expire' => 1440, 'throttle' => 60, ], ], diff --git a/config/jwt.php b/config/jwt.php index 8e92c20..c2d191c 100644 --- a/config/jwt.php +++ b/config/jwt.php @@ -101,7 +101,7 @@ return [ | */ - 'ttl' => env('JWT_TTL', 60), + 'ttl' => env('JWT_TTL', 1440), /* |-------------------------------------------------------------------------- diff --git a/database/migrations/2025_04_23_113243_add_health_flag_to_timeseries_key_master_table.php b/database/migrations/2025_04_23_113243_add_health_flag_to_timeseries_key_master_table.php new file mode 100644 index 0000000..dc60233 --- /dev/null +++ b/database/migrations/2025_04_23_113243_add_health_flag_to_timeseries_key_master_table.php @@ -0,0 +1,28 @@ +tinyInteger('display_on_health_condition')->default(0)->after('display_on_popup'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('timeseries_key_master', function (Blueprint $table) { + $table->dropColumn('display_on_health_condition'); + }); + } +}; diff --git a/database/migrations/2025_04_28_090919_add_display_on_alerts_to_timeseries_key_master_table.php b/database/migrations/2025_04_28_090919_add_display_on_alerts_to_timeseries_key_master_table.php new file mode 100644 index 0000000..ed75807 --- /dev/null +++ b/database/migrations/2025_04_28_090919_add_display_on_alerts_to_timeseries_key_master_table.php @@ -0,0 +1,28 @@ +tinyInteger('display_on_alerts')->default(0)->after('display_on_health_condition'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('timeseries_key_master', function (Blueprint $table) { + $table->dropColumn('display_on_alerts'); + }); + } +}; diff --git a/routes/customer_api.php b/routes/customer_api.php index 47d173a..4ccb756 100644 --- a/routes/customer_api.php +++ b/routes/customer_api.php @@ -44,4 +44,15 @@ Route::middleware(['customerApiBasicAuth'])->group(function () { Route::post('/alarm/filter', [AlarmControllerCommon::class, 'filterAlarm'])->name('alarm.filter'); Route::post('/alarm', [AlarmControllerCommon::class, 'getDeviceAlarms'])->name('alarm.device'); + + // ***************************************** Based on Documentation *********************************************** + + Route::get('/entity-query', [TelemetryController::class, 'entityQuery']); + + Route::get('/user-assets-new', [TelemetryController::class, 'userAssetsNew']); + Route::get('/customer-device-info-new', [TelemetryController::class, 'customerDeviceInfoNew']); + Route::get('/get-device-indicators/{assetId}', [TelemetryController::class, 'getDeviceIndicators']); + Route::get('/get-alerts/{deviceId}', [TelemetryController::class, 'getAlerts']); + Route::get('/get-global-indicators/{deviceId}', [TelemetryController::class, 'getGlobalIndicators']); + Route::get('/get-trends/{deviceId}', [TelemetryController::class, 'getTrends']); });