diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5805153 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +data/* +.Rhistory +apiKey +authTokens.Rda \ No newline at end of file diff --git a/ecobee.R b/ecobee.R new file mode 100644 index 0000000..78d6787 --- /dev/null +++ b/ecobee.R @@ -0,0 +1,220 @@ +#setup ---- +library(tidyverse) +library(lubridate) +library(httr) +library(jsonlite) +library(plotly) +library(scales) + +setwd("~/Documents/dataProjects/ecobee") + +pause = function() +{ + if (interactive()) + { + invisible(readline(prompt = "Press to continue...")) + } + else + { + cat("Press to continue...") + invisible(readLines(file("stdin"), 1)) + } +} + +#make a file called 'apiKey' in your working directory that has your api key for your ecobee +if(file.exists('authTokens.Rda')) { + #if the authToken file already exists, then refresh it + load(file = "authTokens.Rda") + url <- paste0("https://api.ecobee.com/token?grant_type=refresh_token&code=", + authTokens$refreshToken, + "&client_id=", + read_file('apiKey')) + res <- content(httr::GET(url = url)) + authTokens <- data.frame(accessToken = res$access_token, + refreshToken = res$refresh_token) + save(authTokens, file = "authTokens.Rda") + access_token <- authTokens$accessToken + remove(res, url, authTokens) +} else { + #if the authToken file does not exist than set it up, this will require adding the app to your ecobee account + permissions <- 'smartRead' + url <- paste0('https://api.ecobee.com/authorize?response_type=ecobeePin&client_id=',read_file('apiKey'),'&scope=',permissions) + res1 <- content(httr::GET(url = url)) + message(paste("add a new app to your ecobee account and authorize it with this PIN -", res1$ecobeePin)) + + pause() + + url2 <- paste0("https://api.ecobee.com/token?grant_type=ecobeePin&code=",res1$code,"&client_id=",read_file('apiKey')) + res2 <- content(httr::GET(url = url2)) + + authTokens <- data.frame(accessToken = res2$access_token, refreshToken = res2$refresh_token) + save(authTokens, file = "authTokens.Rda") + access_token <- authTokens$accessToken + rm(permissions, url, res1, url2, res2) +} + + +# load data ---- + +startdate <- ymd('2021-11-13') + +enddate <- Sys.Date() +columns <- c('zoneClimate', + 'compHeat1', + 'auxHeat1', + 'compCool1', + 'fan', + 'outdoorTemp', + 'outdoorHumidity', + 'zoneAveTemp', + 'zoneHeatTemp', + 'zoneCoolTemp', + 'zoneHumidity', + 'zoneHvacMode') + +months <- data.frame(start = seq(floor_date(startdate, unit = "months"), + floor_date(enddate, unit = "months"), + length.out = interval(floor_date(startdate, unit = "months"), enddate) %/% months(1) + 1)) +months <- months %>% + mutate(end = start + months(1) - 1) + +ecobee_new <- list(NULL) +for(i in 1:nrow(months)){ + message(paste("Downloading", months$start[[i]], "to", months$end[[i]])) + + url <- paste0('https://api.ecobee.com/1/runtimeReport?format=json&body={"startDate":', + paste0('\"',as.Date(months$start[i]),'\",'), + '"endDate":', + paste0('\"',as.Date(months$end[i]),'\",'), + '"columns":', + paste0('\"',paste0(columns, collapse = ","),'\"'), + ',"selection":{"selectionType":"thermostats","selectionMatch":"413788899054"}}') + ecobee_month <- httr::GET( + url = url, + add_headers('Content-Type' = 'application/json;charset=UTF-8', + 'Authorization' = paste0('Bearer ', access_token) + ) + ) %>% httr::content() + + # parse data ---- + + ecobee_new[[i]] <- tibble(ecobee_month[["reportList"]][[1]][["rowList"]]) %>% + separate(sep = ",", + col = 1, + into = c('date', 'time', columns), + convert=TRUE) %>% + mutate("duration_sec" = 5*60, + "dateTime" = ymd_hms(paste(date, time)), + outdoortemp_bin = round(outdoorTemp)) %>% + filter(ymd(date) <= Sys.Date()) +} +ecobee <- bind_rows(ecobee_new) + +save(ecobee, file = 'data/ecobee.Rda') + +daterange <- data.frame(start = min(ecobee$date), end = max(ecobee$date)) + +# color scale ---- + +temp_types <- data.frame(column = c('zoneAveTemp', 'zoneHeatTemp', 'zoneCoolTemp', 'outdoorTemp'), + label = c('Indoor', 'Set (heat)', 'Set (cool)', 'Outdoor'), + color = c('black', 'orange', 'blue', 'grey')) +equipment_types <- data.frame(column = c('compHeat1', 'auxHeat1', 'compCool1', 'fan'), + label = c('Heat pump (heat)', 'Aux heat', 'Heat pump (cool)', 'Fan'), + color = c('orange', 'red', 'blue', 'grey'), + offset = c(0,1,2,3)) + +equipment_y <- max(ecobee$zoneCoolTemp, ecobee$outdoorTemp, na.rm = TRUE) + 1 + +# make plots ---- + + +ggplot() + + geom_line(data = ecobee %>% pivot_longer(cols = c(zoneAveTemp, outdoorTemp, zoneHeatTemp, zoneCoolTemp), names_to = "temp_type", values_to = "temp"), + aes(x = dateTime, + y = temp, + color = temp_type))+ + geom_rect(data = ecobee %>% pivot_longer(cols = c(compHeat1, auxHeat1, compCool1), names_to = "equipment", values_to = "active_sec") %>% filter(active_sec > 0), + aes(xmin = dateTime, + xmax = dateTime + active_sec, + ymin = equipment_y + equipment_types[match(equipment, equipment_types$column), 4], + ymax = equipment_y + equipment_types[match(equipment, equipment_types$column), 4] + 1, + fill = equipment)) + + scale_color_manual(labels = temp_types$label, values = temp_types %>% pull(color, column)) + + scale_fill_manual(labels = equipment_types$label, values = equipment_types %>% pull(color, column)) + + theme(legend.title = element_blank()) + + labs(x = 'Date', + y = 'Temperature (\u00B0F)') + +ggplot() + + geom_line(data = ecobee %>% filter(date > Sys.Date() - 3) %>% pivot_longer(cols = c(zoneAveTemp, outdoorTemp, zoneHeatTemp, zoneCoolTemp), names_to = "temp_type", values_to = "temp"), + aes(x = dateTime, + y = temp, + color = temp_type))+ + geom_rect(data = ecobee %>% filter(date > Sys.Date() - 3) %>% pivot_longer(cols = c(compHeat1, auxHeat1, compCool1), names_to = "equipment", values_to = "active_sec") %>% filter(active_sec > 0), + aes(xmin = dateTime, + xmax = dateTime + active_sec, + ymin = equipment_y + equipment_types[match(equipment, equipment_types$column), 4], + ymax = equipment_y + equipment_types[match(equipment, equipment_types$column), 4] + 1, + fill = equipment)) + + scale_color_manual(labels = temp_types$label, values = temp_types %>% pull(color, column)) + + scale_fill_manual(labels = equipment_types$label, values = equipment_types %>% pull(color, column)) + + theme(legend.title = element_blank()) + + labs(x = 'Date', + y = 'Temperature (\u00B0F)') + +ggplot(data = ecobee %>% + filter(date != "2021-11-13", + date != today()) %>% + filter(zoneClimate != "") %>% + pivot_longer(cols = c(compHeat1, auxHeat1, compCool1, fan), names_to = "equipment", values_to = "active_sec") %>% + group_by(outdoortemp_bin, equipment, zoneClimate) %>% + summarize(percent_active = sum(active_sec, na.rm = TRUE)/sum(duration_sec, na.rm = TRUE), + n = n()), + aes(x = outdoortemp_bin, + y = percent_active, + color = equipment, + shape = zoneClimate)) + + geom_point(aes(size = n)) + + geom_smooth(aes(linetype = zoneClimate), + se = FALSE) + + scale_color_manual(labels = equipment_types$label, values = equipment_types %>% pull(color, column)) + + scale_y_continuous(labels = label_percent(), expand = expansion(mult = c(0,0))) + + labs(x = 'Outdoor Temperature (\u00B0F)', + y = 'Percent of the time the equipment runs') + +ggplot() + + geom_col(data = ecobee %>% pivot_longer(cols = c(compHeat1, auxHeat1, compCool1), names_to = "equipment", values_to = "active_sec"), + aes(x = as.Date(dateTime) + hours(12), + y = active_sec/60/60, + fill = equipment)) + + scale_fill_manual(labels = equipment_types$label, values = equipment_types %>% pull(color, column)) + + geom_line(data = ecobee, + aes(x = dateTime, + y = outdoorTemp/5, + color = 'Outdoor')) + + labs(x = 'date', + y = 'Time equipment runs (hours)') + + scale_y_continuous(sec.axis = sec_axis(trans = ~ .x*5, + name = "Temperature (\u00B0F)")) + +ggplot(data = ecobee %>% + filter(date != "2021-11-13", + date != today()) %>% + group_by(date) %>% + summarise(outdoorTemp = mean(outdoorTemp), + compHeat1 = sum(compHeat1, na.rm = TRUE), + compCool1 = sum(compCool1, na.rm = TRUE), + auxHeat1 = sum(auxHeat1, na.rm = TRUE)) %>% + pivot_longer(cols = c(compHeat1, compCool1, auxHeat1), names_to = 'equipment', values_to = 'active_sec') %>% + filter(active_sec > 0), + aes(x = outdoorTemp, + y = active_sec/60/60, + color = equipment)) + + geom_point() + + geom_smooth(se = FALSE) + + scale_y_continuous(limits = c(0, 24), expand = expansion(mult = c(0,0))) + + scale_color_manual(labels = equipment_types$label, values = equipment_types %>% pull(color, column)) + + labs(x = 'Outdoor Temperature (\u00B0F)', + y = 'Time equipment runs (hours)') +