{"id":16572567,"url":"https://github.com/davidski/emoncmsr","last_synced_at":"2026-04-24T05:33:02.328Z","repository":{"id":31855208,"uuid":"94958712","full_name":"davidski/emoncmsr","owner":"davidski","description":"🔌Interface library for working with emonCMS energy monitoring data","archived":false,"fork":false,"pushed_at":"2023-09-10T05:25:20.000Z","size":138,"stargazers_count":1,"open_issues_count":1,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-01-16T03:14:23.218Z","etag":null,"topics":["emoncms","openenergymonitor","rstats"],"latest_commit_sha":null,"homepage":"","language":"R","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/davidski.png","metadata":{"files":{"readme":"README.Rmd","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2017-06-21T03:24:35.000Z","updated_at":"2023-09-08T17:26:37.000Z","dependencies_parsed_at":"2022-07-28T12:19:08.449Z","dependency_job_id":"dd257739-980d-48f6-8cf3-282f73df3123","html_url":"https://github.com/davidski/emoncmsr","commit_stats":{"total_commits":34,"total_committers":3,"mean_commits":"11.333333333333334","dds":"0.17647058823529416","last_synced_commit":"43226f5540fd0becc323b90ca569a94696b4af22"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidski%2Femoncmsr","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidski%2Femoncmsr/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidski%2Femoncmsr/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidski%2Femoncmsr/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/davidski","download_url":"https://codeload.github.com/davidski/emoncmsr/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":242041647,"owners_count":20062256,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["emoncms","openenergymonitor","rstats"],"created_at":"2024-10-11T21:27:58.428Z","updated_at":"2026-04-24T05:33:02.302Z","avatar_url":"https://github.com/davidski.png","language":"R","funding_links":[],"categories":[],"sub_categories":[],"readme":"---\noutput:\n  github_document\n---\n\n## `emoncmsr`: R interface to the [emonCMS](https://github.com/emoncms/emoncms) API\n\n\u003c!-- badges: start --\u003e\n[![R build status](https://github.com/davidski/emoncmsr/workflows/R-CMD-check/badge.svg)](https://github.com/davidski/emoncmsr/actions)\n[![Coverage Status](https://img.shields.io/codecov/c/github/davidski/emoncmsr/master.svg)](https://codecov.io/github/davidski/emoncmsr?branch=master)\n\u003c!-- badges: end --\u003e\n\nThis package provides the tools to create, delete, and manage inputs and feeds \nin the open source energy, temperature and environmental monitoring sytem from \n[OpenEnergyMonitor](https://openenergymonitor.org/).\n\nemonCMS has two flavors, a self-hosted version and a hosted solution at \n[emoncms.org](https://emoncms.org). The two APIs are similar but differ in a \nnumber of areas. This package only supports the self-hosted flavor, though \nthere are several unsupported functions for the hosted version.\n\nThis package takes a slightly opinionated view towards the API, returning \nnormalized values from many of the API calls. The native API has several non-\nstandard and inconsistent responses to both success and failure. `emoncmsr` \nprovides an interface consistent with data analysis and a tidy pipeline.\n\n## Installation\n```{r eval=FALSE}\ndevtools::install_github(\"davidski/emoncmsr\")\n```\n\n## Usage\n\nemoncmsr requires two environment variables to locate the emoncms host and the\nproper API key for authentication. These can be set globally (e.g. via \n`.bashrc`, the Windows control panel, or other OS-specific mechanism) or you \ncan place them in your `~.Renviron` file for a portable solution.\n\nEnvironment variables used:\n\n  * EMONCMS_URI - Full URL (w/final slash) to API endpoint\n  * EMONCMS_API_KEY - API key value (read or write) to API\n    \n## Applications\n\nSimulating a beverage sensor that monitors the level of coffee, tea, and \nwater available. First, we'll list the inputs currently configured.\n\n```{r listing_inputs}\nlibrary(emoncmsr)\nsuppressPackageStartupMessages(library(tidyverse))  # use the tidyverse\n\n# Inputs\nlist_inputs()\n```\n\nNow we'll create some simulated data, post it to emonCMS as inputs using a\nnode identifier of `emoncmsr`, read back the value of the coffee level we just \nposted, store the ID of this new coffee input for future use, and set a useful \ndescription for the new input. Whew! Let's get to it!\n\n```{r posting_to_inputs}\n# create some beverage data\ndat \u003c- list(coffee = 42, tea = 6, water = 42)\npost_data_to_input(dat)\nlist_inputs() %\u003e% filter(nodeid == \"emoncmsr\", name == \"coffee\")\n# store the id of the new input\ninputid \u003c- list_inputs() %\u003e% \n  filter(nodeid == \"emoncmsr\", name == \"coffee\") %\u003e% \n  pull(id)\ninputid\n\n# set a friendly description for our new input\nset_input_field(inputid, \"description\", \"cups of coffee remaining in pot\")\nlist_inputs() %\u003e% filter(id == inputid)\n```\n\nThat wasn't so bad! \n\nWhile we're now accepting levels of coffee, tea, and water in emonCMS, those \nvalues aren't being stored or processed in any way. We'd like to monitor the \nlevels of coffee over time. We need to create a feed, then configure the \ncoffee input to send its data to that feed.\n\n```{r creating_the_coffee_feed}\n\n# Create a feed for the coffee level\nfeed_response \u003c- create_feed(\"coffeelevel\", \"emoncmsr\")\nfeed_response\n\n# Show that the feed exists\nlist_feeds() %\u003e% filter(id == feed_response$feedid)\n\n# Hook up the coffee monitor to the feed\nset_input_process(inputid, paste(1, feed_response$feedid, sep = \":\"))\nget_input_processes(inputid)\n```\n\nNow that we have our feed set up, let's send some updated beverage level sensor \ndata. We'll first send a single timepoint set of values, then demonstrate using \nthe bulk data interface to several days of simulated data in a single call. \n\nAfter sending the readings we'll read the feed metadata to demonstrate that data \nhas flowed from the input to the feed.\n\n```{r demonstrate_input_to_feed_posting}\n# Post a single set of readings to all three new inputs\ndat \u003c- list(coffee = 86, tea = 100, water = 4)\npost_data_to_input(dat)\n\n# show the values we just posted appeared in the inputs\nlist_inputs() %\u003e% filter(nodeid == \"emoncmsr\")\n\n# We can also use the bulk data input format for sending a dataframe \n# worth of data, all at different offsets to an optional timestamp\ninterval \u003c- get_feed_metadata(feed_response$feedid)$interval\nend_time \u003c- lubridate::now() %\u003e% as.integer()\nend_time \u003c- end_time - (end_time %% interval)\nstart_time \u003c- (lubridate::now() - lubridate::ddays(3)) %\u003e% as.integer()\nstart_time \u003c- start_time - (start_time %% interval)\n\ntimes \u003c- seq(start_time, end_time, by = interval)\ndat \u003c- tibble(offset = times, nodeid = \"emoncmsr\")\ndat \u003c- bind_cols(dat, tibble(value = map(times, ~list(\"coffee\" = sample(1:10, size=1)))))\npost_bulk_data_to_input(dat, reference_time = 0)\n# this large a bulk post can take a moment to process, sleep for a few seconds\nSys.sleep(5)\n\n# we can also post with a specific reference time, though we don't demonstrate\n# that here...\n# reference_time \u003c- lubridate::as_datetime(\"2017-03-27 01:30:00\") %\u003e% as.integer()\n# post_bulk_data_to_input(dat, reference_time)\n\n\n# show the feed's info\nget_feed_metadata(feed_response$feedid)\n```\n\nBeverage monitoring systems [are GO](https://en.wikipedia.org/wiki/Thunderbirds_Are_Go)! \nLet's pull a set of data from our feed and plot that data over time, adding \na smoothed curve for grins.\n\n```{r plotting_feed_data}\ndat \u003c- get_feed_data(feed_response$feedid)\ngg \u003c- ggplot(dat, aes(x = date, y = value)) + \n  geom_line() + geom_smooth(method = 'loess') +\n  labs(title = \"Coffee Levels\", \n       subtitle = \"Seven day historical with smoothed overlay\",\n       caption = \"Demonstration plot for emoncmsr\",\n       y = \"Watts\", \n       x = NULL) +\n  scale_y_continuous(labels = scales::pretty_breaks()) +\n  theme_minimal()\ngg\n```\n\nFinally, clean up the test inputs and feeds we created.\n\n```{r clean_up}\nlist_feeds() %\u003e% filter(tag == \"emoncmsr\") %\u003e% pull(id) %\u003e% \n  map(~ delete_feed(.x))\nlist_inputs() %\u003e% filter(nodeid == \"emoncmsr\") %\u003e% pull(id) %\u003e% \n  map(~ delete_input(.x))\n```\n\n## Test results\n\n```{r}\nlibrary(emoncmsr)\nlibrary(testthat)\n\ndate()\n\ntest_dir(\"tests/\")\n```\n\n# Contributing\n\nThis project is governed by a [Code of Conduct](./CODE_OF_CONDUCT.md). By \nparticipating in this project you agree to abide by these terms.\n\n# License\n\nThe [MIT License](LICENSE) applies.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavidski%2Femoncmsr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdavidski%2Femoncmsr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavidski%2Femoncmsr/lists"}