aboutsummaryrefslogtreecommitdiffstats
path: root/elisp
diff options
context:
space:
mode:
authorOscar Najera <hi@oscarnajera.com>2024-10-19 20:48:22 +0200
committerOscar Najera <hi@oscarnajera.com>2024-10-19 20:49:43 +0200
commitac2bae6089b758a6ee443bc7b8a98ce6c081ecc9 (patch)
tree06fd6fbfaa55d9340119c1482dd24cbaf8508244 /elisp
parent4f2103feff8bb5a63b9c7024c6a9d1a2d457418e (diff)
downloaddotfiles-ac2bae6089b758a6ee443bc7b8a98ce6c081ecc9.tar.gz
dotfiles-ac2bae6089b758a6ee443bc7b8a98ce6c081ecc9.tar.bz2
dotfiles-ac2bae6089b758a6ee443bc7b8a98ce6c081ecc9.zip
Grocy frontend
Diffstat (limited to 'elisp')
-rw-r--r--elisp/grocy.el149
1 files changed, 149 insertions, 0 deletions
diff --git a/elisp/grocy.el b/elisp/grocy.el
new file mode 100644
index 0000000..90d96a7
--- /dev/null
+++ b/elisp/grocy.el
@@ -0,0 +1,149 @@
+;;; grocy.el --- Grocy frontend -*- lexical-binding: t; -*-
+;;
+;; Copyright (C) 2024 Óscar Nájera
+;;
+;; Author: Óscar Nájera <hi@oscarnajera.com>
+;; Maintainer: Óscar Nájera <hi@oscarnajera.com>
+;; Created: October 19, 2024
+;; Modified: October 19, 2024
+;; Version: 0.0.1
+;; Keywords: abbrev bib c calendar comm convenience data docs emulations extensions faces files frames games hardware help hypermedia i18n internal languages lisp local maint mail matching mouse multimedia news outlines processes terminals tex tools unix vc wp
+;; Homepage: https://github.com/titan/grocy
+;; Package-Requires: ((emacs "27.1"))
+;;
+;; This file is not part of GNU Emacs.
+;;
+;;; Commentary:
+;;
+;; Grocy emacs frontend
+;;
+;;; Code:
+
+;; Silence byte-compiler.
+(defvar url-http-end-of-headers)
+
+(defvar grocy-units)
+(defvar grocy-locations)
+
+(defcustom grocy-server ""
+ "Grocy main domain"
+ :type 'string
+ :group 'grocy)
+
+(defcustom grocy-api-key ""
+ "API key to authenticate to grocy server"
+ :type 'string
+ :group 'grocy)
+
+(defun grocy--request (endpoint callback &optional method data)
+ "Do a GET request to api ENDPOINT and call CALLBACK on the resulting buffer.
+Set METHOD to other request types and include the alredy encoded DATA."
+ (let ((url-request-method (or method "GET"))
+ (url-request-extra-headers
+ `(("GROCY-API-KEY" . ,grocy-api-key)
+ ("accept" . "application/json")
+ ("Content-Type" . "application/json")))
+ (url-request-data data))
+ (url-retrieve
+ (concat grocy-server "/api/" endpoint)
+ callback)))
+
+(defun grocy-update-units ()
+ (grocy--request
+ "objects/quantity_units"
+ (lambda (_status)
+ (goto-char url-http-end-of-headers)
+ (let ((result (make-hash-table)))
+ (mapc
+ (lambda (unit)
+ (-let (((&hash "id" "name" "name_plural") unit))
+ (puthash id
+ (cons name name_plural)
+ result)))
+ (json-parse-buffer))
+ (setq grocy-units result)))))
+
+(defun grocy-update-locations ()
+ (grocy--request
+ "objects/locations"
+ (lambda (_status)
+ (goto-char url-http-end-of-headers)
+ (let ((result (make-hash-table)))
+ (mapc
+ (lambda (location)
+ (-let (((&plist :id) location))
+ (puthash id (cdddr location)
+ result)))
+ (json-parse-buffer :object-type 'plist))
+ (setq grocy-locations result)))))
+
+(defun grocy-stock--row (row)
+ (-let* (((&alist 'amount 'amount_opened 'product 'best_before_date) row)
+ ((&alist 'name 'qu_id_stock) product)
+ (amount
+ (concat (number-to-string amount)
+ " "
+ (funcall
+ (if (= 1 amount) #'car #'cdr)
+ (gethash qu_id_stock grocy-units))
+ (when (< 0 amount_opened)
+ (concat " / "
+ (number-to-string amount_opened)
+ " open")))))
+ (list row (vector best_before_date amount name))))
+
+(defun grocy-stock ()
+ "Show the grocery stock."
+ (interactive)
+ (grocy--request
+ "stock"
+ (lambda (_status)
+ (goto-char url-http-end-of-headers)
+ (let ((result (json-parse-buffer :object-type 'alist)))
+ (with-current-buffer (get-buffer-create "Grocy stock")
+ (tabulated-list-mode)
+ (setq tabulated-list-format [("Next due" 12 t)
+ ("Amount" 20 t)
+ ("Product" 20 t)]
+ ;; tabulated-list-padding 2
+ tabulated-list-sort-key '("Next due"))
+
+ (thread-last
+ (cl-map 'list #'grocy-stock--row result)
+ (setq tabulated-list-entries))
+ (tabulated-list-init-header)
+ (tabulated-list-print)
+ (display-buffer (current-buffer)))))))
+
+(defun grocy-products ()
+ (grocy--request
+ "objects/products"
+ (lambda (_status)
+ (goto-char url-http-end-of-headers)
+ (let ((result (json-parse-buffer)))
+ (with-current-buffer (get-buffer-create "Grocy products")
+ (tabulated-list-mode)
+ (setq tabulated-list-format [("Name" 40 t)
+ ("Location" 15 t)
+ ("Unit" 8 t)
+ ("min stock" 5 t)]
+ tabulated-list-sort-key '("Name"))
+
+ (thread-last
+ (cl-map 'list
+ (lambda (product)
+ (-let (((&hash "name" "location_id" "qu_id_stock"
+ "min_stock_amount") product))
+ (list product
+ (vector name
+ (car (gethash location_id grocy-locations))
+ (car (gethash qu_id_stock grocy-units))
+ (number-to-string min_stock_amount)))))
+ result)
+ (setq tabulated-list-entries))
+ (tabulated-list-init-header)
+ (tabulated-list-print)
+ (display-buffer (current-buffer)))))))
+
+(provide 'grocy)
+;;; grocy.el ends here