@adventurelabs/scout-core 1.4.94 → 1.4.96
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/helpers/sessions.d.ts +2 -0
- package/dist/helpers/sessions.js +14 -0
- package/dist/hooks/index.d.ts +5 -0
- package/dist/hooks/index.js +5 -0
- package/dist/hooks/useScoutRealtimeAnalysisJobs.js +4 -58
- package/dist/hooks/useScoutRealtimeAnalysisTasks.js +4 -58
- package/dist/hooks/useScoutRealtimeBroadcast.d.ts +8 -0
- package/dist/hooks/useScoutRealtimeBroadcast.js +44 -0
- package/dist/hooks/useScoutRealtimeContacts.d.ts +5 -0
- package/dist/hooks/useScoutRealtimeContacts.js +7 -0
- package/dist/hooks/useScoutRealtimeDocuments.d.ts +7 -0
- package/dist/hooks/useScoutRealtimeDocuments.js +22 -0
- package/dist/hooks/useScoutRealtimeMaintenanceRequests.d.ts +5 -0
- package/dist/hooks/useScoutRealtimeMaintenanceRequests.js +7 -0
- package/dist/hooks/useScoutRealtimeParts.js +2 -68
- package/dist/hooks/useScoutRealtimePlans.js +2 -61
- package/dist/hooks/useScoutRealtimeSessionIncidents.d.ts +5 -0
- package/dist/hooks/useScoutRealtimeSessionIncidents.js +7 -0
- package/dist/hooks/useScoutRealtimeVersionsSoftware.js +4 -63
- package/dist/index.d.ts +6 -0
- package/dist/index.js +6 -0
- package/dist/types/supabase.d.ts +57 -6
- package/dist/types/supabase.js +9 -0
- package/package.json +1 -1
|
@@ -2,6 +2,8 @@ import { ISessionWithCoordinates, ISessionUsageOverTime, IEventAndTagsPrettyLoca
|
|
|
2
2
|
import { IWebResponseCompatible } from "../types/requests";
|
|
3
3
|
import { SupabaseClient } from "@supabase/supabase-js";
|
|
4
4
|
export declare function server_get_session_by_id(sessionId: number, client?: SupabaseClient): Promise<IWebResponseCompatible<ISessionWithCoordinates | null>>;
|
|
5
|
+
/** Existing sessions within tolerance of start/end timestamps. */
|
|
6
|
+
export declare function server_match_sessions(deviceId: number, timestampStart: string, timestampEnd: string, toleranceSec: number, client?: SupabaseClient): Promise<IWebResponseCompatible<ISession[]>>;
|
|
5
7
|
export declare function server_get_session_usage_over_time_by_herd(herd_id: number, client?: SupabaseClient, min_duration_minutes_caller?: number, min_distance_meters_caller?: number, product_numbers_caller?: string[]): Promise<IWebResponseCompatible<ISessionUsageOverTime>>;
|
|
6
8
|
export declare function server_get_session_usage_over_time_by_device(device_id: number, client?: SupabaseClient, min_duration_minutes_caller?: number, min_distance_meters_caller?: number, product_numbers_caller?: string[]): Promise<IWebResponseCompatible<ISessionUsageOverTime>>;
|
|
7
9
|
export declare function server_get_events_and_tags_by_session_id(sessionId: number, limit?: number, offset?: number): Promise<IWebResponseCompatible<IEventAndTagsPrettyLocation[]>>;
|
package/dist/helpers/sessions.js
CHANGED
|
@@ -21,6 +21,20 @@ export async function server_get_session_by_id(sessionId, client) {
|
|
|
21
21
|
const session = data && data.length > 0 ? data[0] : null;
|
|
22
22
|
return IWebResponse.success(session).to_compatible();
|
|
23
23
|
}
|
|
24
|
+
/** Existing sessions within tolerance of start/end timestamps. */
|
|
25
|
+
export async function server_match_sessions(deviceId, timestampStart, timestampEnd, toleranceSec, client) {
|
|
26
|
+
const supabase = client || (await newServerClient());
|
|
27
|
+
const { data, error } = await supabase.rpc("match_sessions", {
|
|
28
|
+
device_id_caller: deviceId,
|
|
29
|
+
timestamp_start_caller: timestampStart,
|
|
30
|
+
timestamp_end_caller: timestampEnd,
|
|
31
|
+
tolerance_sec_caller: toleranceSec,
|
|
32
|
+
});
|
|
33
|
+
if (error) {
|
|
34
|
+
return IWebResponse.error(error.message).to_compatible();
|
|
35
|
+
}
|
|
36
|
+
return IWebResponse.success(data || []).to_compatible();
|
|
37
|
+
}
|
|
24
38
|
// Get session usage over time by herd
|
|
25
39
|
export async function server_get_session_usage_over_time_by_herd(herd_id, client, min_duration_minutes_caller, min_distance_meters_caller, product_numbers_caller) {
|
|
26
40
|
const supabase = client || (await newServerClient());
|
package/dist/hooks/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { useScoutRefresh, type UseScoutRefreshOptions, } from "./useScoutRefresh";
|
|
2
|
+
export { useScoutRealtimeBroadcast } from "./useScoutRealtimeBroadcast";
|
|
2
3
|
export { useScoutRealtimeConnectivity } from "./useScoutRealtimeConnectivity";
|
|
3
4
|
export { useScoutRealtimeDevices } from "./useScoutRealtimeDevices";
|
|
4
5
|
export { useScoutRealtimeVersionsSoftware } from "./useScoutRealtimeVersionsSoftware";
|
|
@@ -10,5 +11,9 @@ export { useScoutRealtimePins } from "./useScoutRealtimePins";
|
|
|
10
11
|
export { useScoutRealtimeParts } from "./useScoutRealtimeParts";
|
|
11
12
|
export { useScoutRealtimeAnalysisJobs } from "./useScoutRealtimeAnalysisJobs";
|
|
12
13
|
export { useScoutRealtimeAnalysisTasks } from "./useScoutRealtimeAnalysisTasks";
|
|
14
|
+
export { useScoutRealtimeDocuments, type DocumentRealtimeChannel, } from "./useScoutRealtimeDocuments";
|
|
15
|
+
export { useScoutRealtimeMaintenanceRequests } from "./useScoutRealtimeMaintenanceRequests";
|
|
16
|
+
export { useScoutRealtimeContacts } from "./useScoutRealtimeContacts";
|
|
17
|
+
export { useScoutRealtimeSessionIncidents } from "./useScoutRealtimeSessionIncidents";
|
|
13
18
|
export { useInfiniteSessionsByHerd, useInfiniteSessionsByDevice, useInfiniteEventsByHerd, useInfiniteEventsByDevice, useInfiniteArtifactsByHerd, useInfiniteArtifactsByDevice, useInfiniteFeedByHerd, useInfiniteFeedByDevice, useInfiniteAnalysisJobs, useInfiniteAnalysisTasks, useIntersectionObserver, type UseInfiniteScrollOptions, type UseAnalysisJobsInfiniteOptions, type UseAnalysisTasksInfiniteOptions, } from "./useInfiniteQuery";
|
|
14
19
|
export { useLoadingPerformance, useSessionSummariesByHerd, useHasSessionSummaries, } from "../store/hooks";
|
package/dist/hooks/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { useScoutRefresh, } from "./useScoutRefresh";
|
|
2
|
+
export { useScoutRealtimeBroadcast } from "./useScoutRealtimeBroadcast";
|
|
2
3
|
export { useScoutRealtimeConnectivity } from "./useScoutRealtimeConnectivity";
|
|
3
4
|
export { useScoutRealtimeDevices } from "./useScoutRealtimeDevices";
|
|
4
5
|
export { useScoutRealtimeVersionsSoftware } from "./useScoutRealtimeVersionsSoftware";
|
|
@@ -10,6 +11,10 @@ export { useScoutRealtimePins } from "./useScoutRealtimePins";
|
|
|
10
11
|
export { useScoutRealtimeParts } from "./useScoutRealtimeParts";
|
|
11
12
|
export { useScoutRealtimeAnalysisJobs } from "./useScoutRealtimeAnalysisJobs";
|
|
12
13
|
export { useScoutRealtimeAnalysisTasks } from "./useScoutRealtimeAnalysisTasks";
|
|
14
|
+
export { useScoutRealtimeDocuments, } from "./useScoutRealtimeDocuments";
|
|
15
|
+
export { useScoutRealtimeMaintenanceRequests } from "./useScoutRealtimeMaintenanceRequests";
|
|
16
|
+
export { useScoutRealtimeContacts } from "./useScoutRealtimeContacts";
|
|
17
|
+
export { useScoutRealtimeSessionIncidents } from "./useScoutRealtimeSessionIncidents";
|
|
13
18
|
// RTK Query infinite scroll hooks
|
|
14
19
|
export { useInfiniteSessionsByHerd, useInfiniteSessionsByDevice, useInfiniteEventsByHerd, useInfiniteEventsByDevice, useInfiniteArtifactsByHerd, useInfiniteArtifactsByDevice, useInfiniteFeedByHerd, useInfiniteFeedByDevice, useInfiniteAnalysisJobs, useInfiniteAnalysisTasks, useIntersectionObserver, } from "./useInfiniteQuery";
|
|
15
20
|
// Session summaries and performance hooks
|
|
@@ -1,61 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import {
|
|
3
|
-
import { EnumRealtimeOperation } from "../types/realtime";
|
|
2
|
+
import { useScoutRealtimeBroadcast } from "./useScoutRealtimeBroadcast";
|
|
4
3
|
export function useScoutRealtimeAnalysisJobs(scoutSupabase) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const { payload: data } = payload;
|
|
9
|
-
const jobData = data.record || data.old_record;
|
|
10
|
-
if (!jobData) {
|
|
11
|
-
return;
|
|
12
|
-
}
|
|
13
|
-
let operation;
|
|
14
|
-
switch (data.operation) {
|
|
15
|
-
case "INSERT":
|
|
16
|
-
operation = EnumRealtimeOperation.INSERT;
|
|
17
|
-
break;
|
|
18
|
-
case "UPDATE":
|
|
19
|
-
operation = EnumRealtimeOperation.UPDATE;
|
|
20
|
-
break;
|
|
21
|
-
case "DELETE":
|
|
22
|
-
operation = EnumRealtimeOperation.DELETE;
|
|
23
|
-
break;
|
|
24
|
-
default:
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
console.log(`[scout-core realtime] ANALYSIS_JOB ${data.operation} received for job ${jobData.id}:`, JSON.stringify(jobData));
|
|
28
|
-
const realtimeData = {
|
|
29
|
-
data: jobData,
|
|
30
|
-
operation,
|
|
31
|
-
};
|
|
32
|
-
setLatestJobUpdate(realtimeData);
|
|
33
|
-
}, []);
|
|
34
|
-
const clearLatestUpdate = useCallback(() => {
|
|
35
|
-
setLatestJobUpdate(null);
|
|
36
|
-
}, []);
|
|
37
|
-
useEffect(() => {
|
|
38
|
-
if (!scoutSupabase)
|
|
39
|
-
return;
|
|
40
|
-
channels.current.forEach((channel) => scoutSupabase.removeChannel(channel));
|
|
41
|
-
channels.current = [];
|
|
42
|
-
clearLatestUpdate();
|
|
43
|
-
const channel = scoutSupabase
|
|
44
|
-
.channel("analysis_jobs_changes", { config: { private: true } })
|
|
45
|
-
.on("broadcast", { event: "*" }, handleAnalysisJobBroadcast)
|
|
46
|
-
.subscribe((status) => {
|
|
47
|
-
if (status === "SUBSCRIBED") {
|
|
48
|
-
console.log("[ANALYSIS_JOBS] ✅ Connected to analysis jobs broadcasts");
|
|
49
|
-
}
|
|
50
|
-
else if (status === "CHANNEL_ERROR") {
|
|
51
|
-
console.warn("[ANALYSIS_JOBS] 🟡 Failed to connect to analysis jobs broadcasts");
|
|
52
|
-
}
|
|
53
|
-
});
|
|
54
|
-
channels.current.push(channel);
|
|
55
|
-
return () => {
|
|
56
|
-
channels.current.forEach((ch) => scoutSupabase.removeChannel(ch));
|
|
57
|
-
channels.current = [];
|
|
58
|
-
};
|
|
59
|
-
}, [scoutSupabase, handleAnalysisJobBroadcast, clearLatestUpdate]);
|
|
60
|
-
return [latestJobUpdate, clearLatestUpdate];
|
|
4
|
+
return useScoutRealtimeBroadcast(scoutSupabase, [
|
|
5
|
+
"analysis_jobs_changes",
|
|
6
|
+
]);
|
|
61
7
|
}
|
|
@@ -1,61 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import {
|
|
3
|
-
import { EnumRealtimeOperation } from "../types/realtime";
|
|
2
|
+
import { useScoutRealtimeBroadcast } from "./useScoutRealtimeBroadcast";
|
|
4
3
|
export function useScoutRealtimeAnalysisTasks(scoutSupabase) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const { payload: data } = payload;
|
|
9
|
-
const taskData = data.record || data.old_record;
|
|
10
|
-
if (!taskData) {
|
|
11
|
-
return;
|
|
12
|
-
}
|
|
13
|
-
let operation;
|
|
14
|
-
switch (data.operation) {
|
|
15
|
-
case "INSERT":
|
|
16
|
-
operation = EnumRealtimeOperation.INSERT;
|
|
17
|
-
break;
|
|
18
|
-
case "UPDATE":
|
|
19
|
-
operation = EnumRealtimeOperation.UPDATE;
|
|
20
|
-
break;
|
|
21
|
-
case "DELETE":
|
|
22
|
-
operation = EnumRealtimeOperation.DELETE;
|
|
23
|
-
break;
|
|
24
|
-
default:
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
console.log(`[scout-core realtime] ANALYSIS_TASK ${data.operation} received for task ${taskData.id} (job ${taskData.job_id}):`, JSON.stringify(taskData));
|
|
28
|
-
const realtimeData = {
|
|
29
|
-
data: taskData,
|
|
30
|
-
operation,
|
|
31
|
-
};
|
|
32
|
-
setLatestTaskUpdate(realtimeData);
|
|
33
|
-
}, []);
|
|
34
|
-
const clearLatestUpdate = useCallback(() => {
|
|
35
|
-
setLatestTaskUpdate(null);
|
|
36
|
-
}, []);
|
|
37
|
-
useEffect(() => {
|
|
38
|
-
if (!scoutSupabase)
|
|
39
|
-
return;
|
|
40
|
-
channels.current.forEach((channel) => scoutSupabase.removeChannel(channel));
|
|
41
|
-
channels.current = [];
|
|
42
|
-
clearLatestUpdate();
|
|
43
|
-
const channel = scoutSupabase
|
|
44
|
-
.channel("analysis_tasks_changes", { config: { private: true } })
|
|
45
|
-
.on("broadcast", { event: "*" }, handleAnalysisTaskBroadcast)
|
|
46
|
-
.subscribe((status) => {
|
|
47
|
-
if (status === "SUBSCRIBED") {
|
|
48
|
-
console.log("[ANALYSIS_TASKS] ✅ Connected to analysis tasks broadcasts");
|
|
49
|
-
}
|
|
50
|
-
else if (status === "CHANNEL_ERROR") {
|
|
51
|
-
console.warn("[ANALYSIS_TASKS] 🟡 Failed to connect to analysis tasks broadcasts");
|
|
52
|
-
}
|
|
53
|
-
});
|
|
54
|
-
channels.current.push(channel);
|
|
55
|
-
return () => {
|
|
56
|
-
channels.current.forEach((ch) => scoutSupabase.removeChannel(ch));
|
|
57
|
-
channels.current = [];
|
|
58
|
-
};
|
|
59
|
-
}, [scoutSupabase, handleAnalysisTaskBroadcast, clearLatestUpdate]);
|
|
60
|
-
return [latestTaskUpdate, clearLatestUpdate];
|
|
4
|
+
return useScoutRealtimeBroadcast(scoutSupabase, [
|
|
5
|
+
"analysis_tasks_changes",
|
|
6
|
+
]);
|
|
61
7
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { SupabaseClient } from "@supabase/supabase-js";
|
|
2
|
+
import { Database } from "../types/supabase";
|
|
3
|
+
import { RealtimeData } from "../types/realtime";
|
|
4
|
+
/**
|
|
5
|
+
* Subscribes to private realtime topics and reports the most recent row change across them.
|
|
6
|
+
* Changing the topic set resubscribes and clears the pending change.
|
|
7
|
+
*/
|
|
8
|
+
export declare function useScoutRealtimeBroadcast<T>(scoutSupabase: SupabaseClient<Database>, topics: string[]): [RealtimeData<T> | null, () => void];
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
3
|
+
import { EnumRealtimeOperation } from "../types/realtime";
|
|
4
|
+
const OPERATIONS = {
|
|
5
|
+
INSERT: EnumRealtimeOperation.INSERT,
|
|
6
|
+
UPDATE: EnumRealtimeOperation.UPDATE,
|
|
7
|
+
DELETE: EnumRealtimeOperation.DELETE,
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Subscribes to private realtime topics and reports the most recent row change across them.
|
|
11
|
+
* Changing the topic set resubscribes and clears the pending change.
|
|
12
|
+
*/
|
|
13
|
+
export function useScoutRealtimeBroadcast(scoutSupabase, topics) {
|
|
14
|
+
const [latestUpdate, setLatestUpdate] = useState(null);
|
|
15
|
+
// Key the subscription on the topic set rather than the array identity, so callers can build
|
|
16
|
+
// their topics inline.
|
|
17
|
+
const topicsKey = topics.join("|");
|
|
18
|
+
const subscribedTopics = useMemo(() => topicsKey.split("|").filter(Boolean), [topicsKey]);
|
|
19
|
+
const clearLatestUpdate = useCallback(() => setLatestUpdate(null), []);
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
setLatestUpdate(null);
|
|
22
|
+
if (!scoutSupabase || subscribedTopics.length === 0)
|
|
23
|
+
return;
|
|
24
|
+
const channels = subscribedTopics.map((topic) => scoutSupabase
|
|
25
|
+
.channel(topic, { config: { private: true } })
|
|
26
|
+
.on("broadcast", { event: "*" }, (message) => {
|
|
27
|
+
const change = message.payload;
|
|
28
|
+
const operation = OPERATIONS[change.operation];
|
|
29
|
+
const row = change.record ?? change.old_record;
|
|
30
|
+
if (operation === undefined || !row)
|
|
31
|
+
return;
|
|
32
|
+
setLatestUpdate({ data: row, operation });
|
|
33
|
+
})
|
|
34
|
+
.subscribe((status) => {
|
|
35
|
+
if (status === "CHANNEL_ERROR") {
|
|
36
|
+
console.warn(`[scout-core realtime] 🟡 ${topic} unavailable`);
|
|
37
|
+
}
|
|
38
|
+
}));
|
|
39
|
+
return () => {
|
|
40
|
+
channels.forEach((channel) => scoutSupabase.removeChannel(channel));
|
|
41
|
+
};
|
|
42
|
+
}, [scoutSupabase, subscribedTopics]);
|
|
43
|
+
return [latestUpdate, clearLatestUpdate];
|
|
44
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { SupabaseClient } from "@supabase/supabase-js";
|
|
2
|
+
import { Database } from "../types/supabase";
|
|
3
|
+
import { IContact } from "../types/db";
|
|
4
|
+
import { RealtimeData } from "../types/realtime";
|
|
5
|
+
export declare function useScoutRealtimeContacts(scoutSupabase: SupabaseClient<Database>): [RealtimeData<IContact> | null, () => void];
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useSelector } from "react-redux";
|
|
3
|
+
import { useScoutRealtimeBroadcast } from "./useScoutRealtimeBroadcast";
|
|
4
|
+
export function useScoutRealtimeContacts(scoutSupabase) {
|
|
5
|
+
const activeHerdId = useSelector((state) => state.scout.active_herd_id);
|
|
6
|
+
return useScoutRealtimeBroadcast(scoutSupabase, activeHerdId ? [`${activeHerdId}-contacts`] : []);
|
|
7
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { SupabaseClient } from "@supabase/supabase-js";
|
|
2
|
+
import { Database } from "../types/supabase";
|
|
3
|
+
import { IDocument } from "../types/db";
|
|
4
|
+
import { RealtimeData } from "../types/realtime";
|
|
5
|
+
/** One channel per document owner class, each gated on that class's read ability. */
|
|
6
|
+
export type DocumentRealtimeChannel = "herd" | "device" | "user" | "own" | "product";
|
|
7
|
+
export declare function useScoutRealtimeDocuments(scoutSupabase: SupabaseClient<Database>, channels?: DocumentRealtimeChannel[]): [RealtimeData<IDocument> | null, () => void];
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useSelector } from "react-redux";
|
|
3
|
+
import { useScoutRealtimeBroadcast } from "./useScoutRealtimeBroadcast";
|
|
4
|
+
const ALL_DOCUMENT_CHANNELS = [
|
|
5
|
+
"herd",
|
|
6
|
+
"device",
|
|
7
|
+
"user",
|
|
8
|
+
"own",
|
|
9
|
+
"product",
|
|
10
|
+
];
|
|
11
|
+
export function useScoutRealtimeDocuments(scoutSupabase, channels = ALL_DOCUMENT_CHANNELS) {
|
|
12
|
+
const activeHerdId = useSelector((state) => state.scout.active_herd_id);
|
|
13
|
+
const userId = useSelector((state) => state.scout.user?.id ?? null);
|
|
14
|
+
const topics = channels.flatMap((channel) => {
|
|
15
|
+
if (channel === "product")
|
|
16
|
+
return ["product_documents_changes"];
|
|
17
|
+
if (channel === "own")
|
|
18
|
+
return userId ? [`user_${userId}-documents`] : [];
|
|
19
|
+
return activeHerdId ? [`${activeHerdId}-${channel}_documents`] : [];
|
|
20
|
+
});
|
|
21
|
+
return useScoutRealtimeBroadcast(scoutSupabase, topics);
|
|
22
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { SupabaseClient } from "@supabase/supabase-js";
|
|
2
|
+
import { Database } from "../types/supabase";
|
|
3
|
+
import { IMaintenanceRequest } from "../types/db";
|
|
4
|
+
import { RealtimeData } from "../types/realtime";
|
|
5
|
+
export declare function useScoutRealtimeMaintenanceRequests(scoutSupabase: SupabaseClient<Database>): [RealtimeData<IMaintenanceRequest> | null, () => void];
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useSelector } from "react-redux";
|
|
3
|
+
import { useScoutRealtimeBroadcast } from "./useScoutRealtimeBroadcast";
|
|
4
|
+
export function useScoutRealtimeMaintenanceRequests(scoutSupabase) {
|
|
5
|
+
const activeHerdId = useSelector((state) => state.scout.active_herd_id);
|
|
6
|
+
return useScoutRealtimeBroadcast(scoutSupabase, activeHerdId ? [`${activeHerdId}-maintenance_requests`] : []);
|
|
7
|
+
}
|
|
@@ -1,73 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useSelector } from "react-redux";
|
|
3
|
-
import {
|
|
4
|
-
import { EnumRealtimeOperation } from "../types/realtime";
|
|
3
|
+
import { useScoutRealtimeBroadcast } from "./useScoutRealtimeBroadcast";
|
|
5
4
|
export function useScoutRealtimeParts(scoutSupabase) {
|
|
6
|
-
const channels = useRef([]);
|
|
7
|
-
const [latestPartUpdate, setLatestPartUpdate] = useState(null);
|
|
8
5
|
const activeHerdId = useSelector((state) => state.scout.active_herd_id);
|
|
9
|
-
|
|
10
|
-
const handlePartBroadcast = useCallback((payload) => {
|
|
11
|
-
console.log("[Parts] Broadcast received:", payload.payload.operation);
|
|
12
|
-
const data = payload.payload;
|
|
13
|
-
const partData = data.record || data.old_record;
|
|
14
|
-
if (!partData)
|
|
15
|
-
return;
|
|
16
|
-
let operation;
|
|
17
|
-
switch (data.operation) {
|
|
18
|
-
case "INSERT":
|
|
19
|
-
operation = EnumRealtimeOperation.INSERT;
|
|
20
|
-
console.log("[Parts] New part received:", data.record);
|
|
21
|
-
break;
|
|
22
|
-
case "UPDATE":
|
|
23
|
-
operation = EnumRealtimeOperation.UPDATE;
|
|
24
|
-
console.log("[Parts] Part updated:", data.record);
|
|
25
|
-
break;
|
|
26
|
-
case "DELETE":
|
|
27
|
-
operation = EnumRealtimeOperation.DELETE;
|
|
28
|
-
console.log("[Parts] Part deleted:", data.old_record);
|
|
29
|
-
break;
|
|
30
|
-
default:
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
const realtimeData = {
|
|
34
|
-
data: partData,
|
|
35
|
-
operation,
|
|
36
|
-
};
|
|
37
|
-
console.log(`[scout-core realtime] PART ${data.operation} received:`, JSON.stringify(realtimeData));
|
|
38
|
-
setLatestPartUpdate(realtimeData);
|
|
39
|
-
}, []);
|
|
40
|
-
// Clear latest update
|
|
41
|
-
const clearLatestUpdate = useCallback(() => {
|
|
42
|
-
setLatestPartUpdate(null);
|
|
43
|
-
}, []);
|
|
44
|
-
const cleanupChannels = () => {
|
|
45
|
-
channels.current.forEach((channel) => scoutSupabase.removeChannel(channel));
|
|
46
|
-
channels.current = [];
|
|
47
|
-
};
|
|
48
|
-
const createPartsChannel = (herdId) => {
|
|
49
|
-
return scoutSupabase
|
|
50
|
-
.channel(`${herdId}-parts`, { config: { private: true } })
|
|
51
|
-
.on("broadcast", { event: "*" }, handlePartBroadcast)
|
|
52
|
-
.subscribe((status) => {
|
|
53
|
-
if (status === "SUBSCRIBED") {
|
|
54
|
-
console.log(`[Parts] ✅ Connected to herd ${herdId}`);
|
|
55
|
-
}
|
|
56
|
-
else if (status === "CHANNEL_ERROR") {
|
|
57
|
-
console.warn(`[Parts] 🟡 Failed to connect to herd ${herdId}`);
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
};
|
|
61
|
-
useEffect(() => {
|
|
62
|
-
cleanupChannels();
|
|
63
|
-
// Clear previous update when switching herds
|
|
64
|
-
clearLatestUpdate();
|
|
65
|
-
// Create parts channel for active herd
|
|
66
|
-
if (activeHerdId) {
|
|
67
|
-
const channel = createPartsChannel(activeHerdId);
|
|
68
|
-
channels.current.push(channel);
|
|
69
|
-
}
|
|
70
|
-
return cleanupChannels;
|
|
71
|
-
}, [activeHerdId, clearLatestUpdate]);
|
|
72
|
-
return [latestPartUpdate, clearLatestUpdate];
|
|
6
|
+
return useScoutRealtimeBroadcast(scoutSupabase, activeHerdId ? [`${activeHerdId}-parts`] : []);
|
|
73
7
|
}
|
|
@@ -1,66 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useSelector } from "react-redux";
|
|
3
|
-
import {
|
|
4
|
-
import { EnumRealtimeOperation } from "../types/realtime";
|
|
3
|
+
import { useScoutRealtimeBroadcast } from "./useScoutRealtimeBroadcast";
|
|
5
4
|
export function useScoutRealtimePlans(scoutSupabase) {
|
|
6
|
-
const channels = useRef([]);
|
|
7
|
-
const [latestPlanUpdate, setLatestPlanUpdate] = useState(null);
|
|
8
5
|
const activeHerdId = useSelector((state) => state.scout.active_herd_id);
|
|
9
|
-
|
|
10
|
-
const { payload: data } = message;
|
|
11
|
-
if (data.table !== "plans" || data.schema !== "public") {
|
|
12
|
-
return;
|
|
13
|
-
}
|
|
14
|
-
const planData = data.record ?? data.old_record;
|
|
15
|
-
if (!planData)
|
|
16
|
-
return;
|
|
17
|
-
let operation;
|
|
18
|
-
switch (data.operation) {
|
|
19
|
-
case "INSERT":
|
|
20
|
-
operation = EnumRealtimeOperation.INSERT;
|
|
21
|
-
break;
|
|
22
|
-
case "UPDATE":
|
|
23
|
-
operation = EnumRealtimeOperation.UPDATE;
|
|
24
|
-
break;
|
|
25
|
-
case "DELETE":
|
|
26
|
-
operation = EnumRealtimeOperation.DELETE;
|
|
27
|
-
break;
|
|
28
|
-
default:
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
const realtimeData = {
|
|
32
|
-
data: planData,
|
|
33
|
-
operation,
|
|
34
|
-
};
|
|
35
|
-
setLatestPlanUpdate(realtimeData);
|
|
36
|
-
}, []);
|
|
37
|
-
const clearLatestUpdate = useCallback(() => {
|
|
38
|
-
setLatestPlanUpdate(null);
|
|
39
|
-
}, []);
|
|
40
|
-
useEffect(() => {
|
|
41
|
-
if (!scoutSupabase)
|
|
42
|
-
return;
|
|
43
|
-
channels.current.forEach((channel) => scoutSupabase.removeChannel(channel));
|
|
44
|
-
channels.current = [];
|
|
45
|
-
clearLatestUpdate();
|
|
46
|
-
if (activeHerdId) {
|
|
47
|
-
const channel = scoutSupabase
|
|
48
|
-
.channel(`${activeHerdId}-plans`, { config: { private: true } })
|
|
49
|
-
.on("broadcast", { event: "*" }, handlePlanBroadcast)
|
|
50
|
-
.subscribe((status) => {
|
|
51
|
-
if (status === "SUBSCRIBED") {
|
|
52
|
-
console.log(`[Plans] ✅ Connected to herd ${activeHerdId}`);
|
|
53
|
-
}
|
|
54
|
-
else if (status === "CHANNEL_ERROR") {
|
|
55
|
-
console.warn(`[Plans] 🟡 Failed to connect to herd ${activeHerdId}`);
|
|
56
|
-
}
|
|
57
|
-
});
|
|
58
|
-
channels.current.push(channel);
|
|
59
|
-
}
|
|
60
|
-
return () => {
|
|
61
|
-
channels.current.forEach((ch) => scoutSupabase.removeChannel(ch));
|
|
62
|
-
channels.current = [];
|
|
63
|
-
};
|
|
64
|
-
}, [scoutSupabase, activeHerdId, handlePlanBroadcast, clearLatestUpdate]);
|
|
65
|
-
return [latestPlanUpdate, clearLatestUpdate];
|
|
6
|
+
return useScoutRealtimeBroadcast(scoutSupabase, activeHerdId ? [`${activeHerdId}-plans`] : []);
|
|
66
7
|
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { SupabaseClient } from "@supabase/supabase-js";
|
|
2
|
+
import { Database } from "../types/supabase";
|
|
3
|
+
import { ISessionIncident } from "../types/db";
|
|
4
|
+
import { RealtimeData } from "../types/realtime";
|
|
5
|
+
export declare function useScoutRealtimeSessionIncidents(scoutSupabase: SupabaseClient<Database>): [RealtimeData<ISessionIncident> | null, () => void];
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useSelector } from "react-redux";
|
|
3
|
+
import { useScoutRealtimeBroadcast } from "./useScoutRealtimeBroadcast";
|
|
4
|
+
export function useScoutRealtimeSessionIncidents(scoutSupabase) {
|
|
5
|
+
const activeHerdId = useSelector((state) => state.scout.active_herd_id);
|
|
6
|
+
return useScoutRealtimeBroadcast(scoutSupabase, activeHerdId ? [`${activeHerdId}-session_incidents`] : []);
|
|
7
|
+
}
|
|
@@ -1,66 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import {
|
|
3
|
-
import { EnumRealtimeOperation } from "../types/realtime";
|
|
2
|
+
import { useScoutRealtimeBroadcast } from "./useScoutRealtimeBroadcast";
|
|
4
3
|
export function useScoutRealtimeVersionsSoftware(scoutSupabase) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const handleVersionsSoftwareBroadcast = useCallback((payload) => {
|
|
9
|
-
const { event, payload: data } = payload;
|
|
10
|
-
const versionData = data.record || data.old_record;
|
|
11
|
-
if (!versionData) {
|
|
12
|
-
return;
|
|
13
|
-
}
|
|
14
|
-
let operation;
|
|
15
|
-
switch (data.operation) {
|
|
16
|
-
case "INSERT":
|
|
17
|
-
operation = EnumRealtimeOperation.INSERT;
|
|
18
|
-
break;
|
|
19
|
-
case "UPDATE":
|
|
20
|
-
operation = EnumRealtimeOperation.UPDATE;
|
|
21
|
-
break;
|
|
22
|
-
case "DELETE":
|
|
23
|
-
operation = EnumRealtimeOperation.DELETE;
|
|
24
|
-
break;
|
|
25
|
-
default:
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
console.log(`[scout-core realtime] VERSIONS_SOFTWARE ${data.operation} received for version ${versionData.system}@${versionData.version}:`, JSON.stringify(versionData));
|
|
29
|
-
const realtimeData = {
|
|
30
|
-
data: versionData,
|
|
31
|
-
operation,
|
|
32
|
-
};
|
|
33
|
-
setLatestVersionUpdate(realtimeData);
|
|
34
|
-
}, []);
|
|
35
|
-
// Clear latest update
|
|
36
|
-
const clearLatestUpdate = useCallback(() => {
|
|
37
|
-
setLatestVersionUpdate(null);
|
|
38
|
-
}, []);
|
|
39
|
-
useEffect(() => {
|
|
40
|
-
if (!scoutSupabase)
|
|
41
|
-
return;
|
|
42
|
-
// Clean up existing channels
|
|
43
|
-
channels.current.forEach((channel) => scoutSupabase.removeChannel(channel));
|
|
44
|
-
channels.current = [];
|
|
45
|
-
// Clear previous update when setting up
|
|
46
|
-
clearLatestUpdate();
|
|
47
|
-
// Create versions_software channel
|
|
48
|
-
const channel = scoutSupabase
|
|
49
|
-
.channel("versions_software_changes", { config: { private: true } })
|
|
50
|
-
.on("broadcast", { event: "*" }, handleVersionsSoftwareBroadcast)
|
|
51
|
-
.subscribe((status) => {
|
|
52
|
-
if (status === "SUBSCRIBED") {
|
|
53
|
-
console.log("[VERSIONS_SOFTWARE] ✅ Connected to software versions broadcasts");
|
|
54
|
-
}
|
|
55
|
-
else if (status === "CHANNEL_ERROR") {
|
|
56
|
-
console.warn("[VERSIONS_SOFTWARE] 🟡 Failed to connect to software versions broadcasts");
|
|
57
|
-
}
|
|
58
|
-
});
|
|
59
|
-
channels.current.push(channel);
|
|
60
|
-
return () => {
|
|
61
|
-
channels.current.forEach((ch) => scoutSupabase.removeChannel(ch));
|
|
62
|
-
channels.current = [];
|
|
63
|
-
};
|
|
64
|
-
}, [scoutSupabase, handleVersionsSoftwareBroadcast, clearLatestUpdate]);
|
|
65
|
-
return [latestVersionUpdate, clearLatestUpdate];
|
|
4
|
+
return useScoutRealtimeBroadcast(scoutSupabase, [
|
|
5
|
+
"versions_software_changes",
|
|
6
|
+
]);
|
|
66
7
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export * from "./types/supabase";
|
|
|
10
10
|
export * from "./types/bounding_boxes";
|
|
11
11
|
export * from "./types/events";
|
|
12
12
|
export * from "./types/connectivity";
|
|
13
|
+
export * from "./types/realtime";
|
|
13
14
|
export * from "./types/pubsub_token";
|
|
14
15
|
export * from "./types/client_abilities_token";
|
|
15
16
|
export * from "./types/jwt_mint";
|
|
@@ -73,6 +74,7 @@ export * from "./helpers/session_incidents";
|
|
|
73
74
|
export * from "./helpers/contacts";
|
|
74
75
|
export * from "./helpers/lifecycle";
|
|
75
76
|
export * from "./helpers/storagePath";
|
|
77
|
+
export * from "./hooks/useScoutRealtimeBroadcast";
|
|
76
78
|
export * from "./hooks/useScoutRealtimeConnectivity";
|
|
77
79
|
export * from "./hooks/useScoutRealtimeDevices";
|
|
78
80
|
export * from "./hooks/useScoutRealtimeVersionsSoftware";
|
|
@@ -84,6 +86,10 @@ export * from "./hooks/useScoutRealtimePlans";
|
|
|
84
86
|
export * from "./hooks/useScoutRealtimePins";
|
|
85
87
|
export * from "./hooks/useScoutRealtimeAnalysisJobs";
|
|
86
88
|
export * from "./hooks/useScoutRealtimeAnalysisTasks";
|
|
89
|
+
export * from "./hooks/useScoutRealtimeDocuments";
|
|
90
|
+
export * from "./hooks/useScoutRealtimeMaintenanceRequests";
|
|
91
|
+
export * from "./hooks/useScoutRealtimeContacts";
|
|
92
|
+
export * from "./hooks/useScoutRealtimeSessionIncidents";
|
|
87
93
|
export * from "./hooks/useScoutRefresh";
|
|
88
94
|
export * from "./hooks/useInfiniteQuery";
|
|
89
95
|
export * from "./providers";
|
package/dist/index.js
CHANGED
|
@@ -12,6 +12,7 @@ export * from "./types/supabase";
|
|
|
12
12
|
export * from "./types/bounding_boxes";
|
|
13
13
|
export * from "./types/events";
|
|
14
14
|
export * from "./types/connectivity";
|
|
15
|
+
export * from "./types/realtime";
|
|
15
16
|
export * from "./types/pubsub_token";
|
|
16
17
|
export * from "./types/client_abilities_token";
|
|
17
18
|
export * from "./types/jwt_mint";
|
|
@@ -77,6 +78,7 @@ export * from "./helpers/contacts";
|
|
|
77
78
|
export * from "./helpers/lifecycle";
|
|
78
79
|
export * from "./helpers/storagePath";
|
|
79
80
|
// Hooks
|
|
81
|
+
export * from "./hooks/useScoutRealtimeBroadcast";
|
|
80
82
|
export * from "./hooks/useScoutRealtimeConnectivity";
|
|
81
83
|
export * from "./hooks/useScoutRealtimeDevices";
|
|
82
84
|
export * from "./hooks/useScoutRealtimeVersionsSoftware";
|
|
@@ -88,6 +90,10 @@ export * from "./hooks/useScoutRealtimePlans";
|
|
|
88
90
|
export * from "./hooks/useScoutRealtimePins";
|
|
89
91
|
export * from "./hooks/useScoutRealtimeAnalysisJobs";
|
|
90
92
|
export * from "./hooks/useScoutRealtimeAnalysisTasks";
|
|
93
|
+
export * from "./hooks/useScoutRealtimeDocuments";
|
|
94
|
+
export * from "./hooks/useScoutRealtimeMaintenanceRequests";
|
|
95
|
+
export * from "./hooks/useScoutRealtimeContacts";
|
|
96
|
+
export * from "./hooks/useScoutRealtimeSessionIncidents";
|
|
91
97
|
export * from "./hooks/useScoutRefresh";
|
|
92
98
|
export * from "./hooks/useInfiniteQuery";
|
|
93
99
|
// Providers
|
package/dist/types/supabase.d.ts
CHANGED
|
@@ -490,11 +490,13 @@ export type Database = {
|
|
|
490
490
|
heading: number;
|
|
491
491
|
id: number;
|
|
492
492
|
inserted_at: string;
|
|
493
|
+
link_type: Database["public"]["Enums"]["link_type"];
|
|
493
494
|
location: unknown;
|
|
494
495
|
mode: string | null;
|
|
495
|
-
noise: number;
|
|
496
|
+
noise: number | null;
|
|
497
|
+
quality_normalized: number | null;
|
|
496
498
|
session_id: number | null;
|
|
497
|
-
signal: number;
|
|
499
|
+
signal: number | null;
|
|
498
500
|
timestamp_start: string;
|
|
499
501
|
};
|
|
500
502
|
Insert: {
|
|
@@ -512,11 +514,13 @@ export type Database = {
|
|
|
512
514
|
heading: number;
|
|
513
515
|
id?: number;
|
|
514
516
|
inserted_at?: string;
|
|
517
|
+
link_type?: Database["public"]["Enums"]["link_type"];
|
|
515
518
|
location: unknown;
|
|
516
519
|
mode?: string | null;
|
|
517
|
-
noise
|
|
520
|
+
noise?: number | null;
|
|
521
|
+
quality_normalized?: number | null;
|
|
518
522
|
session_id?: number | null;
|
|
519
|
-
signal
|
|
523
|
+
signal?: number | null;
|
|
520
524
|
timestamp_start: string;
|
|
521
525
|
};
|
|
522
526
|
Update: {
|
|
@@ -534,11 +538,13 @@ export type Database = {
|
|
|
534
538
|
heading?: number;
|
|
535
539
|
id?: number;
|
|
536
540
|
inserted_at?: string;
|
|
541
|
+
link_type?: Database["public"]["Enums"]["link_type"];
|
|
537
542
|
location?: unknown;
|
|
538
543
|
mode?: string | null;
|
|
539
|
-
noise?: number;
|
|
544
|
+
noise?: number | null;
|
|
545
|
+
quality_normalized?: number | null;
|
|
540
546
|
session_id?: number | null;
|
|
541
|
-
signal?: number;
|
|
547
|
+
signal?: number | null;
|
|
542
548
|
timestamp_start?: string;
|
|
543
549
|
};
|
|
544
550
|
Relationships: [
|
|
@@ -4552,6 +4558,47 @@ export type Database = {
|
|
|
4552
4558
|
isSetofReturn: true;
|
|
4553
4559
|
};
|
|
4554
4560
|
};
|
|
4561
|
+
match_sessions: {
|
|
4562
|
+
Args: {
|
|
4563
|
+
device_id_caller: number;
|
|
4564
|
+
timestamp_end_caller: string;
|
|
4565
|
+
timestamp_start_caller: string;
|
|
4566
|
+
tolerance_sec_caller: number;
|
|
4567
|
+
};
|
|
4568
|
+
Returns: {
|
|
4569
|
+
altitude_average: number;
|
|
4570
|
+
altitude_max: number;
|
|
4571
|
+
altitude_min: number;
|
|
4572
|
+
battery_id: number | null;
|
|
4573
|
+
device_id: number;
|
|
4574
|
+
distance_max_from_start: number;
|
|
4575
|
+
distance_total: number;
|
|
4576
|
+
end_approver: string | null;
|
|
4577
|
+
end_reason: Database["public"]["Enums"]["session_end_reason"] | null;
|
|
4578
|
+
id: number;
|
|
4579
|
+
inserted_at: string;
|
|
4580
|
+
lifecycle: Database["public"]["Enums"]["entity_lifecycle"];
|
|
4581
|
+
lifecycle_changed_at: string;
|
|
4582
|
+
lifecycle_changed_by: string | null;
|
|
4583
|
+
lifecycle_reason: string | null;
|
|
4584
|
+
locations: unknown;
|
|
4585
|
+
post_approver: string | null;
|
|
4586
|
+
software_version: string;
|
|
4587
|
+
start_approver: string | null;
|
|
4588
|
+
status: Database["public"]["Enums"]["session_status"];
|
|
4589
|
+
timestamp_end: string | null;
|
|
4590
|
+
timestamp_start: string;
|
|
4591
|
+
velocity_average: number;
|
|
4592
|
+
velocity_max: number;
|
|
4593
|
+
velocity_min: number;
|
|
4594
|
+
}[];
|
|
4595
|
+
SetofOptions: {
|
|
4596
|
+
from: "*";
|
|
4597
|
+
to: "sessions";
|
|
4598
|
+
isOneToOne: false;
|
|
4599
|
+
isSetofReturn: true;
|
|
4600
|
+
};
|
|
4601
|
+
};
|
|
4555
4602
|
migrate_conservaition_users_to_sentala: {
|
|
4556
4603
|
Args: {
|
|
4557
4604
|
preview?: boolean;
|
|
@@ -4761,6 +4808,7 @@ export type Database = {
|
|
|
4761
4808
|
entity_lifecycle: "active" | "retired" | "deleted";
|
|
4762
4809
|
health_item: "healthy" | "repairable" | "unrepairable";
|
|
4763
4810
|
herd_invitation_status: "pending" | "accepted" | "revoked" | "expired";
|
|
4811
|
+
link_type: "wifi" | "ocusync" | "lte" | "satellite" | "mesh" | "lora" | "unknown";
|
|
4764
4812
|
media_type: "image" | "video" | "audio" | "text";
|
|
4765
4813
|
plan_type: "mission" | "fence" | "rally" | "markov";
|
|
4766
4814
|
review_status: "pending" | "annotating" | "in_review" | "completed" | "rejected";
|
|
@@ -4793,6 +4841,8 @@ export type Database = {
|
|
|
4793
4841
|
associated_station: string | null;
|
|
4794
4842
|
mode: string | null;
|
|
4795
4843
|
battery_volts: number | null;
|
|
4844
|
+
link_type: Database["public"]["Enums"]["link_type"] | null;
|
|
4845
|
+
quality_normalized: number | null;
|
|
4796
4846
|
};
|
|
4797
4847
|
device_heartbeat_analysis: {
|
|
4798
4848
|
device_id: number | null;
|
|
@@ -5090,6 +5140,7 @@ export declare const Constants: {
|
|
|
5090
5140
|
readonly entity_lifecycle: readonly ["active", "retired", "deleted"];
|
|
5091
5141
|
readonly health_item: readonly ["healthy", "repairable", "unrepairable"];
|
|
5092
5142
|
readonly herd_invitation_status: readonly ["pending", "accepted", "revoked", "expired"];
|
|
5143
|
+
readonly link_type: readonly ["wifi", "ocusync", "lte", "satellite", "mesh", "lora", "unknown"];
|
|
5093
5144
|
readonly media_type: readonly ["image", "video", "audio", "text"];
|
|
5094
5145
|
readonly plan_type: readonly ["mission", "fence", "rally", "markov"];
|
|
5095
5146
|
readonly review_status: readonly ["pending", "annotating", "in_review", "completed", "rejected"];
|
package/dist/types/supabase.js
CHANGED
|
@@ -31,6 +31,15 @@ export const Constants = {
|
|
|
31
31
|
entity_lifecycle: ["active", "retired", "deleted"],
|
|
32
32
|
health_item: ["healthy", "repairable", "unrepairable"],
|
|
33
33
|
herd_invitation_status: ["pending", "accepted", "revoked", "expired"],
|
|
34
|
+
link_type: [
|
|
35
|
+
"wifi",
|
|
36
|
+
"ocusync",
|
|
37
|
+
"lte",
|
|
38
|
+
"satellite",
|
|
39
|
+
"mesh",
|
|
40
|
+
"lora",
|
|
41
|
+
"unknown",
|
|
42
|
+
],
|
|
34
43
|
media_type: ["image", "video", "audio", "text"],
|
|
35
44
|
plan_type: ["mission", "fence", "rally", "markov"],
|
|
36
45
|
review_status: [
|