import { useHTTP, usePermissionAPI } from "hooks"; import { Component, deviceScope, User } from "models"; import { pond } from "protobuf-ts/pond"; import { createContext, PropsWithChildren, useContext } from "react"; import { pondURL } from "./pond"; import { AxiosResponse } from "axios"; import { useGlobalState } from "providers"; import moment from "moment"; import { or } from "utils/types"; import { dateRange } from "providers/http"; import { getContextKeys, getContextTypes } from "pbHelpers/Context"; export interface IDeviceAPIContext { add: (name: string, description: string, backpack: pond.BackpackSettings, otherTeam?: string) => Promise>; update: (id: number, settings: pond.DeviceSettings, otherTeam?: string) => Promise>; remove: (id: number, otherTeam?: string) => Promise>; get: (id: number | string, demo?: boolean, keys?: string[], types?: string[], otherTeam?: string) => Promise> getPageData: ( id: number | string, keys?: string[], types?: string[], otherTeam?: string ) => Promise>; getMulti: (ids: number[] | string[], otherTeam?: string) => Promise>; detectI2C: (id: number, otherTeam?: string) => Promise>; detectOneWire: (id: number, port: number, otherTeam?: string) => Promise> listFoundComponents: (id: number, otherTeam?: string) => Promise> getGeoJson: (id: number | string, demo?: boolean, otherTeam?: string) => Promise; getMultiGeoJson: (ids: number[] | string[], otherTeam?: string) => Promise; list: ( limit: number, offset: number, order?: "asc" | "desc", orderBy?: string, search?: string, settings?: string, status?: string, ids?: number[] | string[] | Long[], asRoot?: boolean, comprehensive?: boolean, withMeasurements?: boolean, keys?: string[], types?: string[], fieldContains?: Map, prefixSearch?: string, otherTeam?: string ) => Promise>; listForUser: ( asRoot: boolean, user?: string, search?: string, hologram?: boolean, keys?: string[], types?: string[] ) => Promise>; claim: (id: number | Long, body: any) => Promise; listHistory: (id: number, limit: number, offset: number) => Promise; listCompleteHistory: ( id: number, limit: number, offset: number, filters?: pond.ObjectType[] ) => Promise>; updatePermissions: (id: number | string, users: User[]) => Promise; updatePreferences: ( id: number | string, preferences: pond.DevicePreferences, keys?: string[], types?: string[], otherTeam?: string ) => Promise; updateComponentPreferences: ( id: number | string, component: string, preferences: pond.DeviceComponentPreferences, keys?: string[], types?: string[], otherTeam?: string ) => Promise>; listDeviceComponentPreferences: ( id: number | string, keys?: string[], types?: string[], otherTeam?: string ) => Promise>; updateStatus: ( id: number | string, status: pond.DeviceStatus, keys?: string[], types?: string[] ) => Promise>; sync: (id: number) => Promise; clearPending: (id: number) => Promise; pause: (id: number) => Promise; bulkPause: ( ids: number[], setPaused: boolean ) => Promise>; bulkChangeDataCaps: ( ids: number[], newCap: number ) => Promise>; resume: (id: number) => Promise; getUpgradeStatus: (id: number, keys?: string[], types?: string[], otherTeam?: string) => Promise; loadBackpack: (id: number, backpack: number) => Promise; setTags: (id: number, tags: string[]) => Promise; setWifi: ( id: number, gateway: string, password: string, keys?: string[], types?: string[], otherTeam?: string ) => Promise; statistics: ( keys?: string[], types?: string[] ) => Promise; tag: (id: number, tag: string) => Promise; untag: (id: number, tag: string) => Promise; getDatacap: (id: number) => Promise; isOverLimit: (id: number) => Promise; isPaused: (id: number) => Promise; setDatacap: (id: number, newCap: number) => Promise; listJSONMeasurements: ( id: number, components: Component[], startDate: any, endDate: any, limit: number, offset: number, order: string, orderBy: string, otherTeam?: string ) => Promise>; listSimpleJSON: (device: number, otherTeam?: string) => Promise; resetQuackCount: (id: number) => Promise; resetQuackCountTx: (id: number) => Promise; resetQuackCountTx1000: (id: number) => Promise; linearMutation: ( id: number, mutation: pond.LinearMutation ) => Promise>; getDataUsage: (id: number, start?: any) => Promise; buyData: ( id: number | string, MB: number, source?: string, keys?: string[], types?: string[] ) => Promise; removeFoundComponents: (id: number, key: string, otherTeam?: string) => Promise>; } export const DeviceAPIContext = createContext({} as IDeviceAPIContext); interface Props {} export default function DeviceProvider(props: PropsWithChildren) { const { children } = props; const { get, post, put, del } = useHTTP(); const permissionAPI = usePermissionAPI(); const [{ as, team }] = useGlobalState(); const add = (name: string, description: string, backpack: pond.BackpackSettings, otherTeam?: string) => { let url = pondURL("/devices") const view = otherTeam ? otherTeam : as if (view) url = pondURL("/devices?as=" + view) return new Promise>((resolve, reject) => { post(url, { name, description, backpack }).then(resp => { resp.data = pond.AddDeviceResponse.fromObject(resp.data) return resolve(resp) }).catch(err => { return reject(err) }) }) }; const getDevice = ( id: number | string, demo: boolean = false, keys?: string[], types?: string[], otherTeam?: string ) => { const view = otherTeam ? otherTeam : as let url = pondURL( "/devices/" + id + (keys ? "?keys=" + keys.toString() : "") + (types ? "&types=" + types.toString() : ""), demo ); if (view) { url = pondURL( "/devices/" + id + "?as=" + view + (keys ? "&keys=" + keys.toString() : "") + (types ? "&types=" + types.toString() : ""), demo ); } return new Promise>((resolve, reject) => { get(url).then(resp => { resp.data = pond.Device.fromObject(resp.data) return resolve(resp) }).catch(err => { return reject(err) }) }) }; const statistics = ( keys?: string[], types?: string[] ) => { let url = pondURL("/deviceStatistics"); if (types && types.length > 0) { url = pondURL( "/deviceStatistics/" + (keys ? "?keys=" + keys.toString() : "") + (types ? "&types=" + types.toString() : "") ) if (!types.includes("team") && as.length > 0) { url = pondURL( "/deviceStatistics/" + (keys ? "?keys=" + keys.toString() : "") + (types ? "&types=" + types.toString() : "") + ("&as=" + as) ) } } else { if (as.length > 0) { url = pondURL( "/deviceStatistics/?as=" + as ) } } return new Promise>((resolve, reject) => { get(url).then(resp => { resp.data = pond.GetDeviceStatisticsResponse.fromObject(resp.data) return resolve(resp) }).catch(err => { return reject(err) }) }) }; const getDevicePageData = ( id: number | string, keys?: string[], types?: string[], otherTeam?: string ): Promise> => { const view = otherTeam ? otherTeam : as let url = pondURL( "/devicePageData/" + id + (keys ? "?keys=" + keys.toString() : "") + (types ? "&types=" + types.toString() : "") ); if (view && !(types && types.length > 0 && types[0] === "team")) { url = pondURL( "/devicePageData/" + id + "?as=" + view + (keys ? "&keys=" + keys.toString() : "") + (types ? "&types=" + types.toString() : "") ) } return new Promise>((resolve, reject) => { get(url).then(resp => { resp.data = pond.GetDevicePageDataResponse.fromObject(resp.data) return resolve(resp) }).catch(err => { return reject(err) }) }) }; const getDeviceGeoJSON = (id: number | string, demo: boolean = false, otherTeam?: string) => { let url = pondURL("/devices/" + id + "/geojson", demo); const view = otherTeam ? otherTeam : as if (view) url = pondURL("/devices/" + id + "/geojson?as=" + view, demo) return new Promise((resolve, reject) => { get(url).catch(resp => { return resolve(resp) }).catch(err => { return reject(err) }) }) }; const getMulti = (ids: number[] | string[], otherTeam?: string) => { let idString = ""; ids.forEach((id: number | string, i: number) => { if (i === 0) { idString = idString + id; } else { idString = idString + "," + id; } }); const view = otherTeam ? otherTeam : as let url = pondURL("/multidevices?devices=" + idString + (view ? "&as=" + view : "")) return new Promise>((resolve, reject) => { get(url).then(resp => { resp.data = pond.GetMultiDeviceResponse.fromObject(resp.data) return resolve(resp) }).catch(err => { return reject(err) }) }) }; const getMultiDevicesGeoJSON = (ids: number[] | string[], otherTeam?: string) => { let idString = ""; ids.forEach((id: number | string, i: number) => { if (i === 0) { idString = idString + id; } else { idString = idString + "," + id; } }); const view = otherTeam ? otherTeam : as let url = pondURL("/geojson/devices?devices=" + idString + (view ? "&as=" + view : "")) return new Promise((resolve, reject) => { get(url).then(resp => { return resolve(resp) }).catch(err => { return reject(err) }) }) }; const list = ( limit: number, offset: number, order?: "asc" | "desc", orderBy?: string, search?: string, settings?: string, status?: string, ids?: number[] | string[] | Long[], asRoot?: boolean, comprehensive?: boolean, withMeasurements?: boolean, keys?: string[], types?: string[], fieldContains?: Map, prefixSearch?: string, otherTeam?: string ) => { const view = otherTeam ? otherTeam : as const url = pondURL( "/devices" + "?limit=" + limit + "&offset=" + offset + ("&order=" + (order ? order : "asc")) + ("&by=" + (orderBy ? orderBy : "deviceId")) + (search ? "&search=" + search : "") + (settings ? "&settings=" + settings : "") + (status ? "&status=" + status : "") + (ids ? "&ids=" + ids.join(",") : "") + (asRoot ? "&asRoot=" + asRoot.toString() : "") + (view && !(types && types.length > 0 && types[0] === "team") ? "&as=" + view : "") + (comprehensive ? "&comprehensive=" + comprehensive.toString() : "") + (withMeasurements ? "&measurements=" + withMeasurements.toString() : "") + (keys ? "&keys=" + keys.toString() : "") + (types ? "&types=" + types.toString() : "") + (fieldContains ? "&fieldContains=" + JSON.stringify(Object.fromEntries(fieldContains)) : "") + (prefixSearch ? "&prefixSearch=" + prefixSearch : "") ); return new Promise>((resolve, reject)=>{ get(url).then(resp => { resp.data = pond.ListDevicesResponse.fromObject(resp.data) return resolve(resp) }).catch(err => { return reject(err) }); }) }; const listForUser = ( asRoot: boolean, user?: string, search?: string, hologram?: boolean, keys?: string[], types?: string[] ) => { const url = pondURL( "/devices" + (user ? "?as=" + user : "?asRoot=" + asRoot.toString()) + (search ? "&search=" + search : "") + (hologram ? "&hologram=" + hologram.toString() : "") + (keys ? "&keys=" + keys.toString() : "") + (types ? "&types=" + types.toString() : "") ); return new Promise>((resolve, reject) => { get(url).then(resp => { resp.data = pond.ListDevicesResponse.fromObject(resp.data) return resolve(resp) }).catch(err => { return reject(err) }) }) }; const remove = (id: number, otherTeam?: string) => { const view = otherTeam ? otherTeam : as const url = pondURL("/devices/" + id + (view ? "?as=" + view : "")) return new Promise>((resolve, reject) => { del(url).then(resp => { resp.data = pond.RemoveDeviceResponse.fromObject(resp.data) return resolve(resp) }).catch(err => { return reject(err) }) }) }; const update = (id: number, settings: pond.DeviceSettings, otherTeam?: string) => { const view = otherTeam ? otherTeam : as const url = pondURL("/devices/" + id + "/update" + (view ? "?as=" + view : "")); return new Promise>((resolve, reject) => { put(url, settings).then(resp => { resp.data = pond.UpdateDeviceResponse.fromObject(resp) return resolve(resp) }).catch(err => { return reject(err) }) }) }; const claim = (id: number | Long, body: any) => { return new Promise((resolve, reject) => { put(pondURL("/devices/" + id), body).then(resp => { return resolve(resp) }).catch(err => { return reject(err) }) }) }; const listHistory = (id: number, limit: number, offset: number) => { return new Promise((resolve, reject) => { get(pondURL("/devices/" + id + "/history?limit=" + limit + "&offset=" + offset)).then(resp => { return resolve(resp) }).catch(err => { return reject(err) }) }) }; const listCompleteHistory = ( id: number, limit: number, offset: number, filters?: pond.ObjectType[] ) => { let url = pondURL( "/devices/" + id + "/completehistory?limit=" + limit + "&offset=" + offset + (filters ? "&sources=" + filters.toString() : "") ) return new Promise>((resolve, reject) => { get(url).then(resp => { resp.data = pond.ListCompleteHistoryResponse.fromObject(resp.data) return resolve(resp) }).catch(err => { return reject(err) }) }) }; const updateDevicePermissions = (id: number | string, users: User[]) => { return permissionAPI.updatePermissions(deviceScope(id.toString()), users); }; const updatePreferences = ( id: number | string, preferences: pond.DevicePreferences, keys?: string[], types?: string[], otherTeam?: string ) => { const view = otherTeam ? otherTeam : as let url = pondURL( "/devices/" + id + "/preferences" + "?as=" + or(view, "") + (keys ? "&keys=" + keys.toString() : "") + (types ? "&types=" + types.toString() : "") ) return new Promise((resolve, reject) => { put(url,preferences).then(resp => { return resolve(resp) }).catch(err => { return reject(err) }) }) }; const updateComponentPreferences = ( id: number | string, component: string, preferences: pond.DeviceComponentPreferences, keys?: string[], types?: string[], otherTeam?: string ) => { const view = otherTeam ? otherTeam : as let url = pondURL( "/devices/" + id + "/components/" + component + "/componentPreferences" + "?as=" + view + (keys ? "&keys=" + keys.toString() : "") + (types ? "&types=" + types.toString() : "") ) return new Promise>((resolve, reject) => { put(url, preferences).then(resp => { resp.data = pond.UpdateDeviceComponentPreferencesResponse.fromObject(resp.data) return resolve(resp) }).catch(err => { return reject(err) }) }) }; const listDeviceComponentPreferences = ( id: number | string, keys?: string[], types?: string[], otherTeam?: string ) => { const view = otherTeam ? otherTeam : as let url = pondURL( "/devices/" + id + "/componentPreferences" + (keys ? "?keys=" + keys.toString() : "") + (types ? "&types=" + types.toString() : "") ) if (view) { url = pondURL( "/devices/" + id + "/componentPreferences?as=" + view + (keys ? "&keys=" + keys.toString() : "") + (types ? "&types=" + types.toString() : "") ) } return new Promise>((resolve, reject) => { get(url).then(resp => { resp.data = pond.ListDeviceComponentPreferencesResponse.fromObject(resp.data) return resolve(resp) }).catch(err => { return reject(err) }) }) }; const updateStatus = ( id: number | string, status: pond.DeviceStatus, keys?: string[], types?: string[], otherTeam?: string ) => { const view = otherTeam ? otherTeam : as let url = pondURL( "/devices/" + id + "/updateStatus" + "?as=" + view + (keys ? "&keys=" + keys.toString() : "") + (types ? "&types=" + types.toString() : "") ) return new Promise>((resolve, reject) => { put(url, status).then(resp => { resp.data = pond.UpdateDeviceStatusResponse.fromObject(resp.data) return resolve(resp) }).catch(err => { return reject(err) }) }) }; const sync = (id: number) => { return new Promise((resolve, reject) => { post(pondURL("/devices/" + id + "/sync"), {}).then(resp => { return resolve(resp) }).catch(err => { return reject(err) }) }) }; const clearPending = (id: number) => { return new Promise((resolve, reject) => { post(pondURL("/devices/" + id + "/clearPending"), {}).then(resp => { return resolve(resp) }).catch(err => { return reject(err) }) }) }; const pause = (id: number) => { return new Promise((resolve, reject) => { post(pondURL("/devices/" + id + "/pause"), {}).then(resp => { return resolve(resp) }).catch(err => { return reject(err) }) }) }; const bulkPause = (ids: number[], setPaused: boolean) => { let url = pondURL( "/bulkPauseDevices?ids=" + ids.toString() + (setPaused ? "&pause=" + setPaused.toString() : "") ) return new Promise>((resolve, reject) => { post(url,{}).then(resp => { resp.data = pond.BulkPauseDeviceResponse.fromObject(resp.data) return resolve(resp) }).catch(err => { return reject(err) }) }) }; const bulkChangeDataCaps = (ids: number[], newCap: number) => { return new Promise>((resolve, reject) => { post( pondURL("/bulkChangeDataCap?ids=" + ids.toString() + "&dataCap=" + newCap), {} ).then(resp => { resp.data = pond.BulkChangeDataCapsResponse.fromObject(resp.data) return resolve(resp) }).catch(err => { return reject(err) }) }) }; const resume = (id: number) => { return new Promise((resolve, reject) => { post(pondURL("/devices/" + id + "/resume"), {}).then(resp => { return resolve(resp) }).catch(err => { return reject(err) }) }) }; const getUpgradeStatus = (id: number, keys?: string[], types?: string[], otherTeam?: string) => { const view = otherTeam ? otherTeam : as let a = view ? "?as=" + view : ""; if (keys && keys.length > 0 && types && types.length > 0) { a = view ? a + "&keys=" + keys + "&types=" + types : "?keys=" + keys + "&types=" + types; } let url = pondURL("/devices/" + id + "/firmware" + a); return new Promise((resolve, reject) => { get(url).then(resp => { return resolve(resp) }).catch(err => { return reject(err) }) }) }; const loadBackpack = (id: number, backpack: number) => { return new Promise((resolve, reject) => { put(pondURL("/devices/" + id + "/load/" + backpack), {}).then(resp => { return resolve(resp) }).catch(err => { return reject(err) }) }) }; const setTags = (id: number, tags: string[]) => { return new Promise((resolve, reject) => { put(pondURL("/devices/" + id + "/tags"), { tags }).then(resp => { return resolve(resp) }).catch(err => { return reject(err) }) }) }; const setWifi = ( id: number, gateway: string, password: string, keys?: string[], types?: string[], otherTeam?: string ) => { const view = otherTeam ? otherTeam : as let a = view ? "?as=" + view : ""; if (keys && keys.length > 0 && types && types.length > 0) { a = view ? a + "&keys=" + keys + "&types=" + types : "?keys=" + keys + "&types=" + types; } return new Promise((resolve, reject) => { put(pondURL("/devices/" + id + "/wifi" + a), { gateway, password }).then(resp => { return resolve(resp) }).catch(err => { return reject(err) }) }) }; const tag = (id: number, tag: string) => { const keys = getContextKeys() const types = getContextTypes() let url = "/devices/" + id + "/tags/" + tag + "?keys=" + keys + "&types=" + types if (as && !keys.includes(as)) url += "&as=" + as return new Promise((resolve, reject) => { put(pondURL(url), {}).then(resp => { return resolve(resp) }).catch(err => { return reject(err) }) }) }; const untag = (id: number, tag: string) => { return new Promise((resolve, reject) => { del(pondURL("/devices/" + id + "/tags/" + tag)).then(resp => { return resolve(resp) }).catch(err => { return reject(err) }) }) }; const getDatacap = (id: number) => { return new Promise((resolve, reject) => { get(pondURL("/devices/" + id + "/datacap/")).then(resp => { return resolve(resp) }).catch(err => { return reject(err) }) }) }; const isOverLimit = (id: number) => { return new Promise((resolve, reject) => { get(pondURL("/devices/" + id + "/overlimit/")).then(resp => { return resolve(resp) }).catch(err => { return reject(err) }) }) }; const isPaused = (id: number) => { return new Promise((resolve, reject) => { get(pondURL("/devices/" + id + "/paused/")).then(resp => { return resolve(resp) }).catch(err => { return reject(err) }) }) }; const setDatacap = (id: number, newCap: number) => { return new Promise((resolve, reject) => { post(pondURL("/devices/" + id + "/datacap?limit=" + newCap)).then(resp => { return resolve(resp) }).catch(err => { return reject(err) }) }) }; const getDataUsage = (id: number, start?: any) => { if (start) { start = "?start=" + moment(start).toISOString(); } else { start = ""; } return new Promise((resolve, reject) => { get(pondURL("/devices/" + id + "/usage/" + start)).then(resp => { return resolve(resp) }).catch(err => { return reject(err) }) }) }; const listJSONMeasurements = ( id: number, components: Component[], startDate: any, endDate: any, limit: number, offset: number, order: string, orderBy: string, otherTeam?: string ) => { let keyString = ""; components.forEach((comp, i) => { if (i === 0) { keyString = keyString + comp.key(); } else { keyString = keyString + "," + comp.key(); } }); const view = otherTeam ? otherTeam : as if (view) { return get( pondURL( "/devices/" + id + "/measurements/exportComplex" + dateRange(startDate, endDate) + "&components=" + keyString + "&limit=" + limit + "&offset=" + offset + "&order=" + order + "&orderBy=" + orderBy + "&as=" + view ) ); } return get( pondURL( "/devices/" + id + "/measurements/exportComplex" + dateRange(startDate, endDate) + "&components=" + keyString + "&limit=" + limit + "&offset=" + offset + "&order=" + order + "&orderBy=" + orderBy ) ); }; const listSimpleJSON = (device: number, otherTeam?: string) => { let url = "/devices/" + device + "/measurements/exportSimple" const view = otherTeam ? otherTeam : as if(view) url = url + "?as=" + view return new Promise((resolve, reject) => { get(pondURL(url)).then(resp => { return resolve(resp) }).catch(err => { return reject(err) }) }) }; const resetQuackCount = (id: number) => { return new Promise((resolve, reject) => { post(pondURL("/devices/" + id + "/resetCounts")).then(resp => { return resolve(resp) }).catch(err => { return reject(err) }) }) }; const resetQuackCountTx = (id: number) => { return new Promise((resolve, reject) => { post(pondURL("/devices/" + id + "/resetCountTx")).then(resp => { return resolve(resp) }).catch(err => { return reject(err) }) }) }; const resetQuackCountTx1000 = (id: number) => { return new Promise((resolve, reject) => { post(pondURL("/devices/" + id + "/partialResetCountTx")).then(resp => { return resolve(resp) }).catch(err => { return reject(err) }) }) }; const linearMutation = (id: number, mutation: pond.LinearMutation) => { return new Promise>((resolve, reject) => { post( pondURL("/devices/" + id + "/linearMutation"), mutation ).then(resp => { resp.data = pond.DeviceLinearMutationResponse.fromObject(resp.data) return resolve(resp) }).catch(err => { return reject(err) }) }) }; const buyData = ( id: number | string, MB: number, source?: string, keys?: string[], types?: string[], otherTeam?: string ) => { let url = "/devices/" + id + "/buyData?MB=" + MB; const view = otherTeam ? otherTeam : as if (view === team?.key()) url = url + "&team=" + team?.key(); if (source) url = url + "&source=" + source; if (keys) url = url + "&keys=" + keys; if (types) url = url + "&types=" + types; return new Promise((resolve, reject) => { put(pondURL(url)).then(resp => { return resolve(resp) }).catch(err => { return reject(err) }) }) }; const detectI2C = (id: number, otherTeam?: string) => { let url = "/devices/" + id + "/detectI2C"; const view = otherTeam ? otherTeam : as if(view) url = url + "?as=" + view return new Promise>((resolve, reject) => { put(pondURL(url)).then(resp => { return resolve(resp) }).catch(err => { return reject(err) }) }) } const detectOneWire = (id: number, port: number, otherTeam?: string) => { let url = "/devices/" + id + "/detectOneWire?port=" + port; const view = otherTeam ? otherTeam : as if(view) url = url + "?as=" + view return new Promise>((resolve, reject) => { put(pondURL(url)).then(resp => { return resolve(resp) }).catch(err => { return reject(err) }) }) } const listFoundComponents = (id: number, otherTeam?: string) => { let url = "/devices/" + id + "/listScannedComponents"; const view = otherTeam ? otherTeam : as if(view) url = url + "?as=" + view return new Promise>((resolve, reject) => { get(pondURL(url)).then(resp => { return resolve(resp) }).catch(err => { return reject(err) }) }) } const removeFoundComponents = (id: number, key: string, otherTeam?: string) => { let url = "/devices/" + id + "/removeFoundComponents/" + key; const view = otherTeam ? otherTeam : as if(view) url = url + "?as=" + view return new Promise>((resolve, reject) => { del(pondURL(url)).then(resp => { return resolve(resp) }).catch(err => { return reject(err) }) }) } return ( {children} ); } export const useDeviceAPI = () => useContext(DeviceAPIContext); // export const useDeviceWebsocket = ( // id: number | string, // emitter: (m: any) => void, // rate: number = 0 // ) => useWebsocket("/devices/" + id, m => Device.any(JSON.parse(m.data)), emitter, rate);