R/FB_API_calls.R

#' Validate FB Sentry API key
#'
#' Tests the connection to the FB Sentry API
#'
#' @rdname fb_validate_key
#' @author Bill DeVoe, \email{William.DeVoe@@maine.gov}
#' @param url **String** - URL to the FB Sentry API; defaults to version 2 URL.
#' @param key **String** - Name of the Windows environment variable containing
#' the API key.
#' @param stop **Boolean** - If false, the function will return a false value if
#' the connection fails; if true (the default), the function will stop.
#' @return Boolean; true if connection was successful.
#' @export
#' @import httr
#' @import jsonlite
fb_validate_key <- function(url = "https://fb-sentrygps.com/api/v2/",
                            key, stop = T) {
  key_str <- Sys.getenv(key)
  if (key_str == '') {stop("Invalid environment variable for API key.")}
  body <- list(commandstring = "validate_api_key", token = key_str)
  req <- httr::POST(url, body = body, encode = "json")
  httr::stop_for_status(req)
  json <- httr::content(req, "text")
  response <- jsonlite::fromJSON(json)
  if (response$message == "Valid API key.") {
    result = TRUE
    message("Connection to API succeeded.")
  } else {
    result = FALSE
    if (stop == T) {
      stop(paste0("Connection to API failed - ", response$message))
      }
    warning(paste0("Connection to API failed - ", response$message))
  }
  return(result)
}

#' List FB Sentry devices
#'
#' Lists devices for a given FB Sentry API key.
#'
#' @rdname fb_list_devices
#' @author Bill DeVoe, \email{William.DeVoe@@maine.gov}
#' @param url **String** - URL to the FB Sentry API. Defaults to version 2 URL.
#' @param key **String** - Name of the Windows environment variable containing
#' the API key.
#' @return **Dataframe** - Dataframe listing devices.
#' @export
fb_list_devices <- function(url = "https://fb-sentrygps.com/api/v2/", key) {
  key_str <- Sys.getenv(key)
  if (key_str == '') {stop("Invalid environment variable for API key.")}
  # Test API connection
  test <- fb_validate_key(url, key)
  body <- list(commandstring = "get_devices", token = key_str)
  req <- httr::POST(url, body = body, encode = "json")
  httr::stop_for_status(req)
  json <- httr::content(req, "text")
  response <- jsonlite::fromJSON(json)
  return(response$data)
}


#' All FB Sentry data for all devices for date range
#'
#' Pulls all data from FB Sentry API profile for a given date range.
#'
#' @rdname fb_all_data
#' @author Bill DeVoe, \email{William.DeVoe@@maine.gov}
#' @param url **String** - URL to the FB Sentry API; defaults to version 2 URL.
#' @param key **String** - Name of the Windows environment variable containing
#' the API key.
#' @param timebegin **String** - Retrieve reports after this datetime (UTC).
#' Format: MM/dd/yyyy HH:mm:ss
#' @param timeend **String** - Retrieve reports before this datetime (UTC).
#' Format: MM/dd/yyyy HH:mm:ss
#' @param local **Boolean** - Convert timestamps to local timezone. Default True
#' @param short **Boolean** *Optional* - Indicate whether to return short or
#' long version of report; defaults to true (short).
#' @param devices **Character Vector** *Optional* - deviceIDs to include in the
#'  returned data.
#' @return SpatialPoints Dataframe of tracking data.
#' @export
#' @import sp
#' @importFrom methods hasArg
fb_all_data <- function(url = "https://fb-sentrygps.com/api/v2/",
                        key, timebegin, timeend, local = T, short= T, devices) {
  # Check date args
  fb_validate_date(timebegin)
  fb_validate_date(timeend)
  key_str <- Sys.getenv(key)
  if (key_str == '') {stop("Invalid environment variable for API key.")}
  # Set proj4strings variables for WGS84 decimal degrees
  WGS84 = "+init=epsg:4326"
  # Test API connection
  test <- fb_validate_key(url, key)
  message("Downloading data from API...")
  # Define JSON payload
  body <- list(commandstring = "get_reports_all_devices",
               datetime_start = timebegin, datetime_end = timeend,
               coredataonly = short, token = key_str)
  # Execute POST JSON request
  req <- httr::POST(url, body = body, encode = "json")
  httr::stop_for_status(req)
  json <- httr::content(req, "text")
  rm(req)
  # Parse JSON
  response <- jsonlite::fromJSON(json)
  rm(json)
  # Check for errors
  if (length(response$isError) > 0) {
    if (response$isError) {
      stop(response$msg)
    }
  }
  data <- response$data
  rm(response)
  # Filter data by device ID vector if it has been provided.
  if (hasArg(devices)) {
    message("Filtering by deviceId...")
    data <- data[data$deviceId %in% devices,]
  }
  # Parse datetime fields into datetime type
  data[['receivedDate']] <- strptime(data[['receivedDate']],
                                     format='%m/%d/%Y %H:%M:%S')
  data[['updateTime']] <- strptime(data[['updateTime']],
                                   format='%m/%d/%Y %H:%M:%S')
  data$receivedDate <- as.POSIXct(data$receivedDate, tz="UTC")
  data$updateTime <- as.POSIXct(data$updateTime, tz="UTC")
  # Convert to local time
  if (local == T) {
    attributes(data$receivedDate)$tzone <- Sys.timezone()
    attributes(data$updateTime)$tzone <- Sys.timezone()
  }
  # Remove null lat/lons and values outside lat/lon range
  data <- data[data$latitude != 0,]
  data <- data[data$longitude != 0,]
  data <- data[data$longitude <= 180,]
  data <- data[data$longitude >= -180,]
  data <- data[data$latitude <= 90,]
  data <- data[data$latitude >= -90,]
  # Build spatial dataframe out of data table
  sp::coordinates(data) = ~longitude+latitude
  #add WGS84 projection to spatial dataframe
  sp::proj4string(data) = sp::CRS(WGS84)
  # Order data by deviceID and then updatetime (order of track lines)
  data <- data[order(data$deviceId, data$updateTime), ]
  return(data)
}


#' All FB Sentry data for device(s) for a given date range
#'
#' Pulls all data from FB Sentry API profile for a given vector of deviceIDs.
#'
#' @rdname fb_device_data
#' @author Bill DeVoe, \email{William.DeVoe@@maine.gov}
#' @param url **String** - URL to the FB Sentry API; defaults to version 2 URL.
#' @param key **String** - Name of the Windows environment variable containing
#' the API key.
#' @param timebegin **String** - Retrieve reports after this datetime (UTC).
#' Format: MM/dd/yyyy HH:mm:ss
#' @param timeend **String** - Retrieve reports before this datetime (UTC).
#' Format: MM/dd/yyyy HH:mm:ss
#' @param local **Boolean** - Convert timestamps to local timezone.
#' @param short **Boolean** - Indicate whether to return short or long version
#' of report; defaults to true (short).
#' @param devices **Character Vector** - deviceIDs to include in the returned
#' data.
#' @return SpatialPoints Dataframe of tracking data.
#' @export
fb_device_data <- function(url = "https://fb-sentrygps.com/api/v2/", key, timebegin, timeend,
                           local = T, short=T, devices) {
  # Check date args
  fb_validate_date(timebegin)
  fb_validate_date(timeend)
  key_str <- Sys.getenv(key)
  if (key_str == '') {stop("Invalid environment variable for API key.")}
  # Set proj4strings variables for WGS84 decimal degrees
  WGS84 = "+init=epsg:4326"
  # Test API connection
  test <- fb_validate_key(url, key)
  for (device in devices) {
    # Define JSON payload
    body <- list(commandstring = "get_reports_single_device",
                 identifier = device, datetime_start = timebegin,
                 datetime_end = timeend, coredataonly = short, token = key_str)
    # Execute POST JSON request
    req <- httr::POST(url, body = body, encode = "json")
    httr::stop_for_status(req)
    json <- httr::content(req, "text")
    # Parse JSON
    response <- jsonlite::fromJSON(json)
    rm(json)
    # Check for errors
    if (length(response$isError) > 0) {
      if (response$isError) {
        next(paste0(device, " failed. ", response$msg))
      }
    }
    device_data <- response$data
    # If the first device
    if (device == devices[1]) {
      data <- device_data
    }
    else {data <- rbind(data, device_data)}
    rm(device_data)
  }
  # Parse datetime fields into datetime type
  data[['receivedDate']] <- strptime(data[['receivedDate']],
                                     format='%m/%d/%Y %H:%M:%S')
  data[['updateTime']] <- strptime(data[['updateTime']],
                                   format='%m/%d/%Y %H:%M:%S')
  data$receivedDate <- as.POSIXct(data$receivedDate, tz="UTC")
  data$updateTime <- as.POSIXct(data$updateTime, tz="UTC")
  # Convert to local time
  if (local == T) {
    attributes(data$receivedDate)$tzone <- Sys.timezone()
    attributes(data$updateTime)$tzone <- Sys.timezone()
  }
  # Remove null lat/lons and weird values
  data <- data[data$latitude != 0,]
  data <- data[data$longitude != 0,]
  data <- data[data$longitude <= 180,]
  data <- data[data$longitude >= -180,]
  data <- data[data$latitude <= 90,]
  data <- data[data$latitude >= -90,]
  # Build spatial dataframe out of data table
  sp::coordinates(data) = ~longitude+latitude
  # Add WGS84 projection to spatial dataframe
  sp::proj4string(data) = sp::CRS(WGS84)
  # Order data by deviceID and then updatetime (order of track lines)
  data <- data[order(data$deviceId, data$updateTime), ]
  return(data)
}

#' Validate FB Sentry date string
#'
#' Checks that a provided date string is properly formated
#' for the API call.
#'
#' @rdname fb_validate_date
#' @author Bill DeVoe, \email{William.DeVoe@@maine.gov}
#' @param date **String** - Datetime argument
#' @return Boolean; true if properly formatted. An error is thrown
#' if the data is improperly formatted.
fb_validate_date <- function(date) {
  dt <- strptime(x = date, tz = Sys.timezone(), format = "%m/%d/%Y %H:%M:%S")
  if (!(is.na(dt))) {return(T)}
  else {stop(paste0(date," is not properly formatted as MM/dd/yyyy HH:mm:ss"))}
}
bdevoe/VesselTrackeR documentation built on June 1, 2019, 4:58 a.m.