#setup ---- library(tidyverse) library(influxdbclient) library(rmarkdown) if(Sys.info()[4] == "pseudotsuga") { setwd("~/Documents/dataProjects/laundry_status") } else { setwd("/laundry_status") } Sys.setenv(TZ='America/Chicago') # parameters needed to make connection to Database token <- substr(read_file("data/api_key"), 1, 88) org = "home_assistant" bucket = "home_assistant" ## make connection to the influxDB bucket home_assistant <- InfluxDBClient$new(url = "https://influxdb.dendroalsia.net", token = token, org = org) longterm_run_time <- ymd_hms("2024-01-01:00:00:00", tz = "America/Chicago") power_threshold_on <- 10 power_threshold_wash_door <- 3 power_threshold_dry_door <- 1.5 # ---- set variables entities <- data.frame(name = c("washing machine", "dryer"), entity_id = c("washing_machine_power", "dryer_power"), name_es = c("lavadora", "secadora")) colors <- data.frame(value = c("off", "on"), color = c("#bcbcbc", "#008080")) # Define a function to calculate the time difference calculateTimeAgo <- function(eventTime) { current <- Sys.time() # Get the current time timeDiff <- as.duration(current - eventTime) # Calculate the time difference # Convert the time difference to appropriate units and format the result if (timeDiff >= hours(24)) { result <- paste(round(timeDiff / dhours(24)), "days ago") } else if (timeDiff >= hours(1)) { result <- paste(round(timeDiff / dhours(1)), "hours ago") } else if (timeDiff >= minutes(1)) { result <- paste(round(timeDiff / dminutes(1)), "minutes ago") } else { result <- "Just now" } return(result) } calculateTimeAgo_es <- function(eventTime) { current <- Sys.time() # Get the current time timeDiff <- as.duration(current - eventTime) # Calculate the time difference # Convert the time difference to appropriate units and format the result if (timeDiff >= hours(24)) { result <- paste("hace", round(timeDiff / dhours(24)), "dias") } else if (timeDiff >= hours(1)) { result <- paste("hace", round(timeDiff / dhours(1)), "horas") } else if (timeDiff >= minutes(1)) { result <- paste("hace", round(timeDiff / dminutes(1)), "minutos") } else { result <- "Ahora" } return(result) } # ---- update_data function update_data <- function(){ Sys.setlocale("LC_TIME", "en_US.UTF-8") run_time <- Sys.time() start_time <- floor_date(run_time - ddays(7), unit = "days") values <- home_assistant$query(paste0('from(bucket: "home_assistant") |> range(start: ', start_time, ') |> filter(fn: (r) => r["entity_id"] == "washing_machine_power" or r["entity_id"] == "dryer_power") |> filter(fn: (r) => r["_field"] == "value") |> filter(fn: (r) => r["_measurement"] == "W")'), POSIXctCol = NULL) values <- bind_rows(values) values <- values %>% rename(value = "_value", time = "_time") values <- values %>% mutate( time = as.POSIXct(time, tz = "America/Chicago"), status = ifelse(value > power_threshold_on, "on", "off")) %>% mutate(door_threshold = ifelse(entity_id == "washing_machine_power", power_threshold_wash_door, power_threshold_dry_door)) %>% mutate(door = ifelse(value < door_threshold, "open", "closed")) %>% filter(time >= start_time) values_by_entity <- as.list(NULL) for(entity in entities$entity_id) { values_by_entity[[entity]] <- values %>% filter(entity_id %in% entity) %>% mutate(end_time = c(time[-1], run_time)) } values <- bind_rows(values_by_entity) last_change <- values %>% group_by(entity_id, status) %>% slice_max(n = 1, order_by = time) %>% pivot_wider(id_cols = domain, names_from = c(entity_id, status), values_from = time) status_door <- values %>% group_by(entity_id) %>% slice_max(n = 1, order_by = time) %>% select(entity_id, door) %>% pivot_wider(names_from = entity_id, values_from = door) current_status <- as.list(NULL) for (entity in entities$entity_id){ current_status[[entity]] <- ifelse(values %>% filter(entity_id %in% entity) %>% tail(1) %>% pull(value) > power_threshold_on, "on", "off") } current_status <- bind_rows(current_status) current_status_update <<- ifelse((current_status$washing_machine_power == "on")|(current_status$dryer_power) == "on", TRUE, FALSE) ## add spanish translations current_status <- current_status %>% mutate(dryer_power_es = ifelse(dryer_power == "on", "encendida", "apagada"), washing_machine_power_es = ifelse(washing_machine_power == "on", "encendida", "apagada")) status_door <- status_door %>% mutate(dryer_power_es = ifelse(dryer_power == "open", "abierto", "cerado"), washing_machine_power_es = ifelse(washing_machine_power == "open", "abierto", "cerado")) # ---- make plots plot_1day <- ggplot(data = values %>% filter(time >= max(values$end_time) - hours(24))) + geom_tile(aes(x = time + seconds(round(as.numeric(difftime(end_time, time, unit = "secs")))/2), y = entity_id, width = seconds(round(as.numeric(difftime(end_time, time, unit = "secs")))), height = 0.5, fill = status)) + scale_y_discrete(breaks = entities$entity_id, labels = entities$name) + scale_x_datetime(breaks = seq(round_date(max(values$end_time), "4 hours") - hours(24), round_date(max(values$end_time), "4 hours"), by = "4 hours"), date_labels = '%I:%M %p', date_minor_breaks = "1 hours") + scale_fill_manual(values = colors$color) + theme(axis.text.x = element_text(angle = 30, vjust = 0.5)) + labs(title = "Last 24 hours", x = NULL, y = NULL, fill = NULL) plot_1week_days <- ggplot(data = values %>% filter(as.Date(time, tz = "America/Chicago") == as.Date(end_time, tz = "America/Chicago")) %>% mutate(date = as.Date(time, tz = "America/Chicago")) %>% left_join(. , entities, by = join_by(entity_id)) %>% mutate(name = factor(name, levels = c("washing machine", "dryer")))) + geom_rect(aes(xmin = date - hours(8), xmax = date + hours(8), ymin = ymd_hms(paste("2023-01-01", strftime(time, format = "%H:%M:%S"))), ymax = ymd_hms(paste("2023-01-01", strftime(end_time, format = "%H:%M:%S"))), fill = status)) + facet_grid(name ~ .) + scale_y_datetime(date_breaks = "4 hours", date_labels = '%I:%M %p', minor_breaks = "2 hours", expand = expansion(mult = 0)) + scale_x_datetime(date_breaks = "1 day", date_labels = '%A', minor_breaks = NULL) + scale_fill_manual(values = colors$color) + theme(axis.text.x = element_text(angle = 30, vjust = 0.5)) + labs(title = "The past week", x = "Day", y = "Time of Day", fill = NULL) # ---- generate html render("laundry_status.Rmd", output_dir = "html", output_file = "index.html") Sys.setlocale("LC_TIME", "es_NI.UTF-8") # ---- make plots (es) plot_1day_es <- ggplot(data = values %>% filter(time >= max(values$end_time) - hours(24)) %>% mutate(status = ifelse(status == "on", "encendida", "apagada"))) + geom_tile(aes(x = time + seconds(round(as.numeric(difftime(end_time, time, unit = "secs")))/2), y = entity_id, width = seconds(round(as.numeric(difftime(end_time, time, unit = "secs")))), height = 0.5, fill = status)) + scale_y_discrete(breaks = entities$entity_id, labels = entities$name_es) + scale_x_datetime(breaks = seq(round_date(max(values$end_time), "4 hours") - hours(24), round_date(max(values$end_time), "4 hours"), by = "4 hours"), date_labels = '%H:%M', date_minor_breaks = "1 hours") + scale_fill_manual(values = colors$color) + theme(axis.text.x = element_text(angle = 30, vjust = 0.5)) + labs(title = "Hace 24 horas", x = NULL, y = NULL, fill = NULL) plot_1week_days_es <- ggplot(data = values %>% filter(as.Date(time, tz = "America/Chicago") == as.Date(end_time, tz = "America/Chicago")) %>% mutate(date = as.Date(time, tz = "America/Chicago")) %>% left_join(. , entities, by = join_by(entity_id)) %>% mutate(name = factor(name_es, levels = c("lavadora", "secadora"))) %>% mutate(status = ifelse(status == "on", "encendida", "apagada"))) + geom_rect(aes(xmin = date - hours(8), xmax = date + hours(8), ymin = ymd_hms(paste("2023-01-01", strftime(time, format = "%H:%M:%S"))), ymax = ymd_hms(paste("2023-01-01", strftime(end_time, format = "%H:%M:%S"))), fill = status)) + facet_grid(name ~ .) + scale_y_datetime(date_breaks = "4 hours", date_labels = '%H:%M', minor_breaks = "2 hours", expand = expansion(mult = 0)) + scale_x_datetime(date_breaks = "1 day", date_labels = '%A', minor_breaks = NULL) + scale_fill_manual(values = colors$color) + theme(axis.text.x = element_text(angle = 30, vjust = 0.5)) + labs(title = "La semana pasada", x = "Día", y = "Tiempo", fill = NULL) render("laundry_status_es.Rmd", output_dir = "html", output_file = "es.html") } update_longterm_data <- function(){ Sys.setlocale("LC_TIME", "en_US.UTF-8") run_time <- Sys.time() longterm_run_time <<- run_time start_time <- floor_date(run_time - ddays(365), unit = "days") values <- home_assistant$query(paste0('from(bucket: "home_assistant") |> range(start: ', start_time, ') |> filter(fn: (r) => r["entity_id"] == "washing_machine_power" or r["entity_id"] == "dryer_power") |> filter(fn: (r) => r["_field"] == "value") |> filter(fn: (r) => r["_measurement"] == "W")'), POSIXctCol = NULL) values <- bind_rows(values) values <- values %>% rename(value = "_value", time = "_time") values <- values %>% mutate( time = as.POSIXct(time, tz = "America/Chicago"), status = ifelse(value > power_threshold_on, "on", "off")) %>% mutate(door_threshold = ifelse(entity_id == "washing_machine_power", power_threshold_wash_door, power_threshold_dry_door)) %>% mutate(door = ifelse(value < door_threshold, "open", "closed")) %>% filter(time >= start_time) values_by_entity <- as.list(NULL) for(entity in entities$entity_id) { values_by_entity[[entity]] <- values %>% filter(entity_id %in% entity) %>% mutate(end_time = c(time[-1], run_time)) } values <- bind_rows(values_by_entity) values["weekday"] <- wday(values$time, label = TRUE) values["hourofday"] <- floor_date(ymd_hms(paste("2023_01_01",strftime(values$time, format="%H:%M:%S"))), unit = "hours") long_term <- values %>% filter(status =="on") %>% group_by(weekday, hourofday, entity_id, status) %>% summarise(count = n()) # ---- make plots plot_all_week_days <- ggplot(data = long_term %>% left_join(. , entities, by = join_by(entity_id)) %>% mutate(name = factor(name, levels = c("washing machine", "dryer")))) + geom_tile(aes(x = weekday, y = hourofday, alpha = count, fill = status)) + facet_grid(name ~ .) + scale_y_datetime(date_breaks = "4 hours", date_labels = '%I:%M %p', minor_breaks = "2 hours", expand = expansion(mult = 0)) + scale_x_discrete() + scale_fill_manual(values = colors$color[2], guide = NULL) + theme(axis.text.x = element_text(angle = 30, vjust = 0.5)) + labs(title = "Long term trends", subtitle = paste0("data from ",strftime(min(values$time), format = "%B %d, %Y"), " to ", strftime(run_time, format = "%B %d, %Y")), x = "Weekday", y = "Time of Day", alpha = "Frequency") # ---- generate html render("laundry_longterm_data.Rmd", output_dir = "html", output_file = "longterm_data.html") # ---- spanish translation Sys.setlocale("LC_TIME", "es_NI.UTF-8") values["weekday"] <- wday(values$time, label = TRUE) values["hourofday"] <- floor_date(ymd_hms(paste("2023_01_01",strftime(values$time, format="%H:%M:%S"))), unit = "hours") long_term <- values %>% filter(status =="on") %>% group_by(weekday, hourofday, entity_id, status) %>% summarise(count = n()) # ---- make plots plot_all_week_days_es <- ggplot(data = long_term %>% left_join(. , entities, by = join_by(entity_id)) %>% mutate(name = factor(name_es, levels = c("lavadora", "secadora")))) + geom_tile(aes(x = weekday, y = hourofday, alpha = count, fill = status)) + facet_grid(name ~ .) + scale_y_datetime(date_breaks = "4 hours", date_labels = '%H:%M', minor_breaks = "2 hours", expand = expansion(mult = 0)) + scale_x_discrete() + scale_fill_manual(values = colors$color[2], guide = NULL) + theme(axis.text.x = element_text(angle = 30, vjust = 0.5)) + labs(title = "Tendencias a largo plazo", subtitle = paste0("datos desde el ",strftime(min(values$time), format = "%d de %B de %Y"), " hasta el ", strftime(run_time, format = "%d de %B de %Y")), x = "Día de la semana", y = "Tiempo", alpha = "Frecuencia") render("laundry_longterm_data_es.Rmd", output_dir = "html", output_file = "longterm_data_es.html") } continue <- TRUE while(continue){ message(Sys.time()) update_data() if (as.double(difftime(Sys.time(), longterm_run_time, units = "hours", tz = "America/Chicago")) > 24) { update_longterm_data() } if (current_status_update == TRUE) { message("sleeping for 1 minute") Sys.sleep(60*1) } else { message("sleeping for 5 minutes") Sys.sleep(60*5) } }