Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# Changes in version 1.0.5 (release date:??-??-2025)

- Fitbit: Now also loads heart rate data #78

- ActiWatch: Enable recognition of file extension when filename has more than one dot.

# Changes in version 1.0.4 (release date:31-03-2025)

- Fitbit: Fix bug preventing the loading of a sequence of json files. #76
Expand Down
2 changes: 1 addition & 1 deletion R/getExtension.R
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ getExtension <- function(filename){
# Extract file extension
ex <- unlist(strsplit(basename(filename), split = "[.]"))
if (length(ex) < 2) stop(paste0("Cannot recognise extension from '", filename, "' as filename, please check"), call. = FALSE)
return(ex[-1])
return(ex[length(ex)])
}


Expand Down
28 changes: 26 additions & 2 deletions R/mergeFitbitData.R
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,36 @@ mergeFitbitData = function(filenames = NULL, desiredtz = "", configtz = NULL) {
stop("Provide at least two filenames")
}
cnt = 1

# change order filenames such that
# heart rate if present is at the end, because heart rate
# has irregular epoch length and needs to be interpolated
filenames = filenames[c(grep(pattern = "sleep", x = basename(filenames), invert = FALSE),
grep(pattern = "calories", x = basename(filenames), invert = FALSE),
grep(pattern = "step", x = basename(filenames), invert = FALSE),
grep(pattern = "heart", x = basename(filenames), invert = FALSE))]

while (cnt <= length(filenames)) {
D = readFitbit(filename = filenames[cnt], desiredtz = desiredtz, configtz = configtz)
if (cnt == 1) {
data = D
} else {
if (length(grep("heart", x = colnames(D))) > 0) {
# interpolate heart rate to 30 seconds to ease merging with
# other data types
t0 = round(as.numeric(D$dateTime[1]) / 30) * 30
t1 = round(as.numeric(D$dateTime[nrow(D)]) / 30) * 30
newTime = seq(t0, t1, by = 30)
newD = as.data.frame(resample(raw = as.matrix(D$heart_rate),
rawTime = as.numeric(D$dateTime),
time = newTime,
stop = nrow(D)))
colnames(newD)[1] = "heart_rate"
if (length(newTime) > nrow(newD)) newTime = newTime[1:nrow(newD)]
newD$dateTime = as.POSIXct(newTime, tz = desiredtz)
rm(D)
D = newD[, c("dateTime", "heart_rate")]
}
# double names is possible when recording is split across json files
# in that case there may be multiple calories, steps and sleep files
doubleNames = colnames(D)[colnames(D) %in% colnames(data)]
Expand Down Expand Up @@ -43,8 +68,7 @@ mergeFitbitData = function(filenames = NULL, desiredtz = "", configtz = NULL) {
cnt = cnt + 1
}
data = data[order(data$dateTime),]

# fill gaps
# fill gaps with NA values
timeRange = range(data$dateTime)
epochSize = min(diff(as.numeric(data$dateTime[1:pmin(10, nrow(data))])))
timeFrame = data.frame(dateTime = seq( timeRange[1], timeRange[2], by = epochSize))
Expand Down
9 changes: 9 additions & 0 deletions R/readFitbit.R
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,15 @@ readFitbit = function(filename = NULL, desiredtz = "",
D = handleTimeGaps(data, epochSize = 30)
D$value = as.numeric(D$value) / 2
colnames(D)[2] = dataType
} else if (dataType == "heart_rate") {
collapseHR = function(x) {
y = data.frame(dateTime = x$dateTime, bpm = x$value$bpm, confidence = x$value$confidence)
return(y)
}
D = as.data.frame(data.table::rbindlist(lapply(D, collapseHR), fill = TRUE))
D = D[!duplicated(D),]
D$dateTime = as.POSIXct(D$dateTime, format = "%m/%d/%y %H:%M:%S", tz = configtz)
colnames(D)[2] = dataType
} else {
stop("File type not recognised")
}
Expand Down
Loading