Browse Source

initial commit

master
boB Rudis 2 years ago
parent
commit
311d5d70bd
No known key found for this signature in database GPG Key ID: 1D7529BE14E2BBA9
16 changed files with 477 additions and 16 deletions
  1. +1
    -0
      .Rbuildignore
  2. +7
    -5
      DESCRIPTION
  3. +2
    -0
      LICENSE
  4. +21
    -0
      LICENSE.md
  5. +5
    -2
      NAMESPACE
  6. +16
    -7
      R/deere-package.R
  7. +52
    -0
      R/src_mowerplus.R
  8. +14
    -0
      R/utils.R
  9. +89
    -1
      README.Rmd
  10. +203
    -0
      README.md
  11. BIN
      README_files/figure-gfm/mow-1.png
  12. BIN
      README_files/figure-gfm/mow-2.png
  13. BIN
      README_files/figure-gfm/mow-3.png
  14. +17
    -1
      man/deere.Rd
  15. +16
    -0
      man/from_coredata_ts.Rd
  16. +34
    -0
      man/src_mowerplus.Rd

+ 1
- 0
.Rbuildignore View File

@ -15,3 +15,4 @@
^CONDUCT.*$
^CODE.*$
^\.gitlab-ci\.yml$
^LICENSE\.md$

+ 7
- 5
DESCRIPTION View File

@ -1,6 +1,6 @@
Package: deere
Type: Package
Title: deere title goes here otherwise CRAN checks fail
Title: Catchall Functions for All Things 'John Deere'
Version: 0.1.0
Date: 2019-06-10
Authors@R: c(
@ -8,18 +8,20 @@ Authors@R: c(
comment = c(ORCID = "0000-0001-5670-2640"))
)
Maintainer: Bob Rudis <bob@rud.is>
Description: A good description goes here otherwise CRAN checks fail.
Description: Initially a convenience package to access 'John Deere' 'MowerPlus' databases
from 'iOS' backups but perpaps will be something more all-encompassing.
URL: https://gitlab.com/hrbrmstr/deere
BugReports: https://gitlab.com/hrbrmstr/deere/issues
Encoding: UTF-8
License: AGPL
License: MIT + file LICENSE
Suggests:
testthat,
covr
Depends:
R (>= 3.2.0)
Imports:
httr,
jsonlite
tibble,
dplyr,
RSQLite
Roxygen: list(markdown = TRUE)
RoxygenNote: 6.1.1

+ 2
- 0
LICENSE View File

@ -0,0 +1,2 @@
YEAR: 2019
COPYRIGHT HOLDER: Bob Rudis

+ 21
- 0
LICENSE.md View File

@ -0,0 +1,21 @@
# MIT License
Copyright (c) 2019 Bob Rudis
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

+ 5
- 2
NAMESPACE View File

@ -1,4 +1,7 @@
# Generated by roxygen2: do not edit by hand
import(httr)
importFrom(jsonlite,fromJSON)
export(from_coredata_ts)
export(src_mowerplus)
import(tibble)
importFrom(dplyr,src_sqlite)
importFrom(dplyr,tbl)

+ 16
- 7
R/deere-package.R View File

@ -1,12 +1,21 @@
#' ...
#'
#' Catchall Functions for All Things 'John Deere'
#'
#' Initially a convenience package to access 'John Deere' 'MowerPlus' databases from
#' 'iOS' backups but perpaps will be something more all-encompassing.
#'
#' Ref:
#'
#' - <https://rud.is/b/2019/06/02/trawling-through-ios-backups-for-treasure-a-k-a-how-to-fish-for-target-files-in-ios-backups-with-r/>
#' - <https://rud.is/b/2019/06/09/wrapping-up-exploration-of-john-deeres-mowerplus-database/>
#'
#'
#' - URL: <https://gitlab.com/hrbrmstr/deere>
#' - BugReports: <https://gitlab.com/hrbrmstr/deere/issues>
#'
#'
#' @md
#' @name deere
#' @docType package
#' @keywords internal
#' @author Bob Rudis (bob@@rud.is)
#' @import httr
#' @importFrom jsonlite fromJSON
NULL
#' @import tibble
#' @importFrom dplyr tbl src_sqlite
"_PACKAGE"

+ 52
- 0
R/src_mowerplus.R View File

@ -0,0 +1,52 @@
#' Find and sync a copy of the latest MowerPlus database file from an iOS backup
#'
#' @md
#' @note You may need to [setup permissions](https://rud.is/b/2019/06/02/trawling-through-ios-backups-for-treasure-a-k-a-how-to-fish-for-target-files-in-ios-backups-with-r/)
#' to be able to use this method depending on which macOS version you're on.
#' @param backup_id the giant hex string of a folder name
#' @param data_loc where `mowtrack.sqlite` will be sync'd
#' @param overwrite nuke ^^ if present (def: `TRUE`)
#' @export
#' @examples \dontrun{
#' mow_db <- src_mowerplus("28500cd31b9580aaf5815c695ebd3ea5f7455628")
#'
#' mow_db
#'
#' glimpse(tbl(mow_db, "ZMOWER"))
#'
#' glimpse(tbl(mow_db, "ZACTIVITY"))
#'
#' }
src_mowerplus <- function(backup_id, data_loc = "~/Data", overwrite = TRUE) {
# root of mobile backup dir for `backup_id`
mb <- path.expand(file.path("~/Library/Application Support/MobileSync/Backup", backup_id))
stopifnot(dir.exists(mb))
data_loc <- path.expand(data_loc)
stopifnot(dir.exists(data_loc))
tf <- tempfile(fileext = ".sqlite")
on.exit(unlink(tf), add=TRUE)
# path to the extracted sqlite file
out_db <- file.path(data_loc, "mowtrack.sqlite")
file.copy(file.path(mb, "Manifest.db"), tf, overwrite = TRUE)
manifest_db <- src_sqlite(tf)
fils <- tbl(manifest_db, "Files")
filter(fils, relativePath == "Library/Application Support/MowTracking.sqlite") %>%
pull(fileID) -> mowtrackdb_loc
file.copy(
file.path(mb, sprintf("%s/%s", substr(mowtrackdb_loc, 1, 2), mowtrackdb_loc)),
file.path(data_loc, "mowtrack.sqlite"),
overwrite = overwrite
)
src_sqlite(out_db)
}

+ 14
- 0
R/utils.R View File

@ -0,0 +1,14 @@
#' Convert timestampes from Apple "CoreData" format to something usable
#'
#' @md
#' @param x timestamps in Apple "CoreData" format(dates or times)
#' @param tz passed on to the convertion to a `POSIXct` object. Def: `NULL`.
#' @export
from_coredata_ts <- function(x, tz = NULL) {
.POSIXct(ifelse(
test = floor(log10(x)) >= 10, # If you're still using R in 2317 then good on ya and edit this
yes = as.POSIXct(x/10e8, origin = "2001-01-01"), # nanoseconds coredata
no = as.POSIXct(x, origin = "2001-01-01") # seconds coredata
), tz = tz)
}

+ 89
- 1
README.Rmd View File

@ -4,7 +4,7 @@ editor_options:
chunk_output_type: inline
---
```{r pkg-knitr-opts, include=FALSE}
knitr$opts_chunk$set(collapse=TRUE, fig.retina=2, message=FALSE, warning=FALSE)
knitr::opts_chunk$set(collapse=TRUE, fig.retina=2, message=FALSE, warning=FALSE)
options(width=120)
```
@ -14,8 +14,17 @@ options(width=120)
# deere
Catchall Functions for All Things 'John Deere'
## Description
Initially a convenience package to access 'John Deere' 'MowerPlus' databases from 'iOS' backups but perpaps will be something more all-encompassing.
Ref:
- <https://rud.is/b/2019/06/02/trawling-through-ios-backups-for-treasure-a-k-a-how-to-fish-for-target-files-in-ios-backups-with-r/>
- <https://rud.is/b/2019/06/09/wrapping-up-exploration-of-john-deeres-mowerplus-database/>
## What's Inside The Tin
The following functions are implemented:
@ -38,12 +47,91 @@ devtools::install_github("hrbrmstr/deere")
```{r lib-ex}
library(deere)
library(hrbrthemes)
library(tidyverse)
# current version
packageVersion("deere")
```
### Sample from a recent mow
```{r mow, cache=TRUE}
mow_db <- src_mowerplus("28500cd31b9580aaf5815c695ebd3ea5f7455628")
mow_db
glimpse(tbl(mow_db, "ZMOWER"))
glimpse(tbl(mow_db, "ZACTIVITY"))
tbl(mow_db, "ZACTIVITY")%>%
collect() -> activity
activity %>%
select(
mow_date = ZCREATEDAT,
area_covered = ZAREACOVERED,
avg_speed = ZAVERAGESPEED,
distance = ZDISTANCEMOWED,
duration = ZMOWINGTIME
) %>%
arrange(mow_date) %>%
mutate(
duration = duration / 60 / 60, # hours
mow_date = format(from_coredata_ts(mow_date), "%b %d"), # factors make better bars
mow_date = factor(mow_date, levels = unique(mow_date)) # when there are just 2-of-em
) %>%
gather(measure, value, -mow_date) %>%
ggplot(aes(mow_date, value)) +
geom_col(aes(fill = measure), width = 0.5, show.legend = FALSE) +
scale_y_comma() +
scale_fill_ipsum() +
facet_wrap(~measure, scales = "free") +
theme_ipsum_rc(grid="Y")
zloc <- tbl(mow_db, "ZMOWLOCATION")
zloc %>%
select(
id = ZSESSION,
zorder = ZORDER,
lat = ZLATITUDE,
lng = ZLONGITUDE,
speed = ZSPEED,
ts = ZTIMESTAMP
) %>%
collect() %>%
mutate(
id = factor(id),
ts = from_coredata_ts(ts)
) -> sessions
ggplot(sessions, aes(id, speed)) +
ggbeeswarm::geom_quasirandom(
aes(fill = id), show.legend = FALSE,
shape = 21, size = 2, color = "white", stroke = 0.75
) +
scale_fill_ipsum() +
labs(x = "Mowing Session", y = "MPH", title = "Mowing Speed Comparison (mph)") +
theme_ipsum_rc(grid="Y")
arrange(sessions, ts) %>%
ggplot(aes(lng, lat)) +
geom_path(
aes(color = id, group = id), show.legend = FALSE,
size = 1, alpha = 1/2
) +
scale_color_ipsum() +
coord_quickmap() +
facet_wrap(~id) +
labs(title = "Mowing Path Comparison") +
theme_ipsum_rc(grid="Y") +
ggthemes::theme_map()
```
## deere Metrics
```{r cloc, echo=FALSE}


+ 203
- 0
README.md View File

@ -1,2 +1,205 @@
[![Travis-CI Build
Status](https://travis-ci.org/hrbrmstr/deere.svg?branch=master)](https://travis-ci.org/hrbrmstr/deere)
[![Coverage
Status](https://codecov.io/gh/hrbrmstr/deere/branch/master/graph/badge.svg)](https://codecov.io/gh/hrbrmstr/deere)
[![CRAN\_Status\_Badge](http://www.r-pkg.org/badges/version/deere)](https://cran.r-project.org/package=deere)
# deere
Catchall Functions for All Things ‘John Deere’
## Description
Initially a convenience package to access ‘John Deere’ ‘MowerPlus’
databases from ‘iOS’ backups but perpaps will be something more
all-encompassing.
Ref:
- <https://rud.is/b/2019/06/02/trawling-through-ios-backups-for-treasure-a-k-a-how-to-fish-for-target-files-in-ios-backups-with-r/>
- <https://rud.is/b/2019/06/09/wrapping-up-exploration-of-john-deeres-mowerplus-database/>
## What’s Inside The Tin
The following functions are implemented:
## Installation
``` r
devtools::install_git("https://git.sr.ht/~hrbrmstr/deere.git")
# or
devtools::install_git("https://git.rud.is/hrbrmstr/deere.git")
# or
devtools::install_gitlab("hrbrmstr/deere")
# or
devtools::install_bitbucket("hrbrmstr/deere")
# or
devtools::install_github("hrbrmstr/deere")
```
## Usage
``` r
library(deere)
library(hrbrthemes)
library(tidyverse)
# current version
packageVersion("deere")
## [1] '0.1.0'
```
### Sample from a recent mow
``` r
mow_db <- src_mowerplus("28500cd31b9580aaf5815c695ebd3ea5f7455628")
mow_db
## src: sqlite 3.22.0 [/Users/hrbrmstr/Data/mowtrack.sqlite]
## tbls: Z_METADATA, Z_MODELCACHE, Z_PRIMARYKEY, ZACTIVITY, ZDEALER, ZMOWALERT, ZMOWER, ZMOWLOCATION, ZSMARTCONNECTOR,
## ZUSER
glimpse(tbl(mow_db, "ZMOWER"))
## Observations: ??
## Variables: 23
## Database: sqlite 3.22.0 [/Users/hrbrmstr/Data/mowtrack.sqlite]
## $ Z_PK <int> 1
## $ Z_ENT <int> 7
## $ Z_OPT <int> 11
## $ ZDECKSIZEINCHES <int> 48
## $ ZDISMISSEDFULLSERVICETASK <int> 0
## $ ZDISMISSEDPERIODICTASK <int> 0
## $ ZSMARTCONNECTOR <int> NA
## $ ZUSER <int> 1
## $ ZBATTERYCHARGE <dbl> NA
## $ ZENGINEHOURS <dbl> 3.474705
## $ ZFULLSERVICEPERFORMED <dbl> NA
## $ ZHMCLASTSEEN <dbl> NA
## $ ZHMCOFFSET <dbl> 0
## $ ZPERIODICSERVICEPERFORMED <dbl> NA
## $ ZSCLASTCONNECTED <dbl> NA
## $ ZGENERICTYPE <chr> NA
## $ ZHMCIDENTIFIER <chr> NA
## $ ZMODEL <chr> "E140"
## $ ZSCPIN <chr> NA
## $ ZSCPERIPHERALID <chr> NA
## $ ZSERIALNUMBER <chr> "1GXE140EKKK116940"
## $ ZSERIES <chr> "E100"
## $ ZSCDATADICTIONARY <blob> <NA>
glimpse(tbl(mow_db, "ZACTIVITY"))
## Observations: ??
## Variables: 20
## Database: sqlite 3.22.0 [/Users/hrbrmstr/Data/mowtrack.sqlite]
## $ Z_PK <int> 1, 2
## $ Z_ENT <int> 3, 3
## $ Z_OPT <int> 124, 93
## $ ZMONTH <int> 6, 6
## $ ZYEAR <int> 2019, 2019
## $ ZMOWER <int> 1, 1
## $ ZUSER <int> 1, 1
## $ ZISCOMPLETE <int> 1, 1
## $ ZISMISSEDMOW <int> 0, 0
## $ ZLASTLOCATION <int> 7016, 12548
## $ ZCREATEDAT <dbl> 581100260, 581778616
## $ ZENGINEHOURS <dbl> NA, NA
## $ ZAREACOVERED <dbl> 3.761875, 2.286811
## $ ZAVERAGESPEED <dbl> 3.727754, 2.894269
## $ ZDISTANCEMOWED <dbl> 7.758894, 4.716564
## $ ZMOWINGTIME <dbl> 6960.000, 5548.939
## $ ZNOTES <chr> "First mow!", NA
## $ ZINTERVALNAME <chr> NA, NA
## $ ZTYPE <chr> NA, NA
## $ ZUUID <blob> blob[238 B], blob[238 B]
tbl(mow_db, "ZACTIVITY")%>%
collect() -> activity
activity %>%
select(
mow_date = ZCREATEDAT,
area_covered = ZAREACOVERED,
avg_speed = ZAVERAGESPEED,
distance = ZDISTANCEMOWED,
duration = ZMOWINGTIME
) %>%
arrange(mow_date) %>%
mutate(
duration = duration / 60 / 60, # hours
mow_date = format(from_coredata_ts(mow_date), "%b %d"), # factors make better bars
mow_date = factor(mow_date, levels = unique(mow_date)) # when there are just 2-of-em
) %>%
gather(measure, value, -mow_date) %>%
ggplot(aes(mow_date, value)) +
geom_col(aes(fill = measure), width = 0.5, show.legend = FALSE) +
scale_y_comma() +
scale_fill_ipsum() +
facet_wrap(~measure, scales = "free") +
theme_ipsum_rc(grid="Y")
```
<img src="README_files/figure-gfm/mow-1.png" width="672" />
``` r
zloc <- tbl(mow_db, "ZMOWLOCATION")
zloc %>%
select(
id = ZSESSION,
zorder = ZORDER,
lat = ZLATITUDE,
lng = ZLONGITUDE,
speed = ZSPEED,
ts = ZTIMESTAMP
) %>%
collect() %>%
mutate(
id = factor(id),
ts = from_coredata_ts(ts)
) -> sessions
ggplot(sessions, aes(id, speed)) +
ggbeeswarm::geom_quasirandom(
aes(fill = id), show.legend = FALSE,
shape = 21, size = 2, color = "white", stroke = 0.75
) +
scale_fill_ipsum() +
labs(x = "Mowing Session", y = "MPH", title = "Mowing Speed Comparison (mph)") +
theme_ipsum_rc(grid="Y")
```
<img src="README_files/figure-gfm/mow-2.png" width="672" />
``` r
arrange(sessions, ts) %>%
ggplot(aes(lng, lat)) +
geom_path(
aes(color = id, group = id), show.legend = FALSE,
size = 1, alpha = 1/2
) +
scale_color_ipsum() +
coord_quickmap() +
facet_wrap(~id) +
labs(title = "Mowing Path Comparison") +
theme_ipsum_rc(grid="Y") +
ggthemes::theme_map()
```
<img src="README_files/figure-gfm/mow-3.png" width="672" />
## deere Metrics
| Lang | \# Files | (%) | LoC | (%) | Blank lines | (%) | \# Lines | (%) |
| :--- | -------: | ---: | --: | ---: | ----------: | ---: | -------: | ---: |
| Rmd | 1 | 0.17 | 74 | 0.69 | 31 | 0.69 | 39 | 0.45 |
| R | 5 | 0.83 | 33 | 0.31 | 14 | 0.31 | 48 | 0.55 |
## Code of Conduct
Please note that this project is released with a [Contributor Code of
Conduct](CONDUCT.md). By participating in this project you agree to
abide by its terms.

BIN
README_files/figure-gfm/mow-1.png View File

Before After
Width: 1344  |  Height: 960  |  Size: 55 KiB

BIN
README_files/figure-gfm/mow-2.png View File

Before After
Width: 1344  |  Height: 960  |  Size: 267 KiB

BIN
README_files/figure-gfm/mow-3.png View File

Before After
Width: 1344  |  Height: 960  |  Size: 575 KiB

+ 17
- 1
man/deere.Rd View File

@ -4,13 +4,29 @@
\name{deere}
\alias{deere}
\alias{deere-package}
\title{...}
\title{Catchall Functions for All Things 'John Deere'}
\description{
Initially a convenience package to access 'John Deere' 'MowerPlus' databases from
'iOS' backups but perpaps will be something more all-encompassing.
}
\details{
Ref:
\itemize{
\item \url{https://rud.is/b/2019/06/02/trawling-through-ios-backups-for-treasure-a-k-a-how-to-fish-for-target-files-in-ios-backups-with-r/}
\item \url{https://rud.is/b/2019/06/09/wrapping-up-exploration-of-john-deeres-mowerplus-database/}
\item URL: \url{https://gitlab.com/hrbrmstr/deere}
\item BugReports: \url{https://gitlab.com/hrbrmstr/deere/issues}
}
}
\seealso{
Useful links:
\itemize{
\item \url{https://gitlab.com/hrbrmstr/deere}
\item Report bugs at \url{https://gitlab.com/hrbrmstr/deere/issues}
}
}
\author{
Bob Rudis (bob@rud.is)
}
\keyword{internal}

+ 16
- 0
man/from_coredata_ts.Rd View File

@ -0,0 +1,16 @@
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/utils.R
\name{from_coredata_ts}
\alias{from_coredata_ts}
\title{Convert timestampes from Apple "CoreData" format to something usable}
\usage{
from_coredata_ts(x, tz = NULL)
}
\arguments{
\item{x}{timestamps in Apple "CoreData" format(dates or times)}
\item{tz}{passed on to the convertion to a \code{POSIXct} object. Def: \code{NULL}.}
}
\description{
Convert timestampes from Apple "CoreData" format to something usable
}

+ 34
- 0
man/src_mowerplus.Rd View File

@ -0,0 +1,34 @@
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/src_mowerplus.R
\name{src_mowerplus}
\alias{src_mowerplus}
\title{Find and sync a copy of the latest MowerPlus database file from an iOS backup}
\usage{
src_mowerplus(backup_id, data_loc = "~/Data", overwrite = TRUE)
}
\arguments{
\item{backup_id}{the giant hex string of a folder name}
\item{data_loc}{where \code{mowtrack.sqlite} will be sync'd}
\item{overwrite}{nuke ^^ if present (def: \code{TRUE})}
}
\description{
Find and sync a copy of the latest MowerPlus database file from an iOS backup
}
\note{
You may need to \href{https://rud.is/b/2019/06/02/trawling-through-ios-backups-for-treasure-a-k-a-how-to-fish-for-target-files-in-ios-backups-with-r/}{setup permissions}
to be able to use this method depending on which macOS version you're on.
}
\examples{
\dontrun{
mow_db <- src_mowerplus("28500cd31b9580aaf5815c695ebd3ea5f7455628")
mow_db
glimpse(tbl(mow_db, "ZMOWER"))
glimpse(tbl(mow_db, "ZACTIVITY"))
}
}

Loading…
Cancel
Save