Skip to content

Commit

Permalink
v2.2.3 (#25)
Browse files Browse the repository at this point in the history
- Added tests
- Use `pop-to-buffer` and window properties to improve dashboard behavior.
  • Loading branch information
purplg authored Aug 13, 2022
1 parent 9de2671 commit c6d9bd2
Show file tree
Hide file tree
Showing 15 changed files with 555 additions and 106 deletions.
85 changes: 85 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
name: Tests

on:
push:
branches: [ "dev" ]

pull_request:
branches: [ "master", "dev" ]

workflow_dispatch:

jobs:
build-home-assistant:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Setup Docker buildx
uses: docker/setup-buildx-action@v2

- name: Build
uses: docker/build-push-action@v2
with:
context: tests/docker
file: tests/docker/Dockerfile
tags: hass-test-homeassistant:latest
outputs: type=docker,dest=/tmp/hass-test-homeassistant.tar

- name: Upload container
uses: actions/upload-artifact@v2
with:
name: hass-test-homeassistant
path: /tmp/hass-test-homeassistant.tar

test:
runs-on: ubuntu-latest
needs: build-home-assistant

strategy:
matrix:
emacs_version:
- 25.1
- 25.2
- 25.3
- 26.1
- 26.2
- 26.3
- 27.1
- 27.2
- 28.1
- snapshot

steps:

# Prepare Home Assistant docker
- name: Setup Docker buildx
uses: docker/setup-buildx-action@v2

- name: Download container
uses: actions/download-artifact@v2
with:
name: hass-test-homeassistant
path: /tmp

- name: Import Docker image
run: |
docker load --input /tmp/hass-test-homeassistant.tar
docker image ls -a
# Prepare Emacs
- uses: purcell/setup-emacs@master
with:
version: ${{ matrix.emacs_version }}

- uses: actions/checkout@v3

- name: deps
run: make deps

# Run tests
- name: start Home Assistant
run: make start-homeassistant

- name: test
run: make test
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,8 @@ project.org
*.~undo-tree

# End of https://www.toptal.com/developers/gitignore/api/elisp

/tests/eldev
# Added automatically by ‘eldev init’.
/.eldev
/Eldev-local
19 changes: 19 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
docker-name := hass-test-homeassistant
emacs := emacs -Q --batch -l targets/melpa.el

.PHONY: test
test:
${emacs} -L ./ -l ./tests/test-*.el --eval="(ert-run-tests-batch-and-exit)"

.PHONY: start-homeassistant
start-homeassistant:
docker run -d --rm -p 8123:8123 --name ${docker-name} ${docker-name} && \
timeout 10 bash -c "until curl http://localhost:8123/api/ --silent > /dev/null ; do sleep 0.2 ; done"

.PHONY: build-homeassistant
build-homeassistant:
docker build -t ${docker-name} tests/docker/

.PHONY: deps
deps:
${emacs} -l targets/deps.el
3 changes: 1 addition & 2 deletions README.org
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,6 @@ Retrieve your API key a.k.a. /Long-Lived Access Token/ by logging into your Home
:label "Vacuum"
:confirm "Start vacuuming? ") ;; Ask for confirmation with a custom prompt

;; Ask for confirmation with a custom prompt
("vacuum.valetudo_vacuum"
:label "Vacuum return home"
:service "vacuum.return_to_base" ;; Call this service instead of the default one to start cleaning
Expand All @@ -217,7 +216,7 @@ To use the dashboard feature, ~hass-dash-layout~ must be configure to tell ~hass
|---------------------+----------------------------------------------------------------------------------------------|
| ~:label~ | The human readable label of the widget to be shown on the dashboard. |
| ~:service~ | The service to be called when the widget is selected. |
| ~:icon~ | The icon to be shown prefix to the widget. |
| ~:icon~ | The icon to be shown prefixed to the widget. |
| ~:state~ | An entity id of the state to be shown next to the widget. |
| ~:widget-formatter~ | The function used to format the widgets on the dashboard. |
| ~:label-formatter~ | The function used to format the label of the widget. |
Expand Down
72 changes: 37 additions & 35 deletions hass-dash.el
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,10 @@
;; configure how the dashboard is displayed.

;;; Code:
(require 'hass)
(require 'subr-x)

(require 'hass)


;; Customizable
(defvar hass-dash-mode-map
Expand Down Expand Up @@ -175,6 +176,7 @@ Full example:
:group 'hass-dash
:type 'list)


;; Default formatters
(defcustom hass-dash-default-widget-formatter #'hass-dash-widget-formatter
"The function called to format the widgets on the dashboard."
Expand Down Expand Up @@ -215,10 +217,7 @@ Full example:


;; Dashboard rendering
(defun hass-dash-widget-formatter (label state icon
label-formatter
state-formatter
icon-formatter)
(defun hass-dash-widget-formatter (label state icon label-formatter state-formatter icon-formatter)
"Default constructor for a widget.
This function composes a widget in the way it should be shown on the dashboard
buffer.
Expand Down Expand Up @@ -249,7 +248,8 @@ STATE is a string of the current state of the widget to be rendered."
ICON is the icon of the widget to be rendered."
(concat icon " "))

(cl-defun hass-dash--create-widget (entity-id &key
(cl-defun hass-dash--create-widget (entity-id
&key
(service (hass-dash--default-service-of entity-id))
;; `:name' keyword is deprecated. Use `:label' instead.
(name (or (plist-get (cdr (assoc entity-id hass--available-entities))
Expand Down Expand Up @@ -298,22 +298,24 @@ When CONFIRM is non-nil a prompt will ask for confirmation before the SERVICE
is called. A string of will be used for a custom prompt. If a function is
passed then the service will only be called when the function returns t."
(widget-create 'push-button
:tag (funcall widget-formatter label (hass-state-of state) icon
label-formatter state-formatter icon-formatter)
:format (if service "%[%t%]" "%t")
:action (cond ((stringp confirm)
(lambda (&rest _)
(when (y-or-n-p confirm)
(hass-call-service entity-id service))))
((functionp confirm)
(lambda (&rest _)
(when (funcall confirm entity-id)
(hass-call-service entity-id service))))
(confirm
(lambda (&rest _)
(when (y-or-n-p (concat "Toggle " name "? "))
(hass-call-service entity-id service))))
((lambda (&rest _) (hass-call-service entity-id service))))))
:tag (funcall widget-formatter label (hass-state-of state) icon
label-formatter state-formatter icon-formatter)
:format (if service "%[%t%]" "%t")
:action (cond ((stringp confirm)
(lambda (&rest _)
(when (y-or-n-p confirm)
(hass-call-service entity-id service nil))))
((functionp confirm)
(lambda (&rest _)
(when (funcall confirm entity-id)
(hass-call-service entity-id service nil))))
(confirm
(lambda (&rest _)
(when (y-or-n-p (concat "Toggle " name "? "))
(hass-call-service entity-id service nil))))
(t
(lambda (&rest _)
(hass-call-service entity-id service nil))))))

(defun hass-dash--insert-groups ()
"Insert all widgets in `hass-dash-layout'."
Expand All @@ -324,35 +326,35 @@ passed then the service will only be called when the function returns t."
(insert "\n")
(dolist (widget (cdr group))
(unless (when-let ((hide-fn (plist-get (cdr widget) ':hide-fn)))
(funcall hide-fn widget))
(funcall hide-fn widget))
(apply 'hass-dash--create-widget widget)
(insert "\n")))
(insert "\n"))))


;; User functions
;;;###autoload
(defun hass-dash-refresh ()
"Rerender the hass-dash buffer."
(interactive)
(with-current-buffer (get-buffer-create hass-dash-buffer-name)
(let ((inhibit-read-only t)
(prev-line (line-number-at-pos)))
(erase-buffer)
(hass-dash--insert-groups)
(goto-char (point-min))
(forward-line (1- prev-line))
(hass-dash-mode))))
(let ((inhibit-read-only t)
(prev-line (line-number-at-pos)))
(erase-buffer)
(hass-dash--insert-groups)
(goto-char (point-min))
(forward-line (1- prev-line))
(hass-dash-mode))))

;;;###autoload
(defun hass-dash-open ()
"Open the hass-dash buffer."
(interactive)
(hass-dash-refresh)
(when-let ((dash-buffer (get-buffer-create hass-dash-buffer-name)))
(if-let ((window (get-buffer-window dash-buffer)))
(select-window window)
(switch-to-buffer-other-window dash-buffer))))
(let* ((buffer (get-buffer hass-dash-buffer-name))
(window (get-buffer-window buffer)))
(pop-to-buffer buffer)
(set-window-dedicated-p window t)))


(define-derived-mode hass-dash-mode special-mode "Home Assistant Dash"
Expand Down
32 changes: 18 additions & 14 deletions hass-websocket.el
Original file line number Diff line number Diff line change
Expand Up @@ -53,17 +53,21 @@
;; (hass-websocket-mode t)

;;; Code:
(require 'hass)
(require 'json)

;; Check if the websocket package exists. Halt loading rest of package if it doesn't.
(unless (require 'websocket nil 'noerror)
(user-error "`hass-websocket-mode' requires package `websocket'"))

(require 'json)

(require 'hass)

;; User customizable
(defvar hass-websocket-mode-map (make-sparse-keymap)
"Keymap for `hass-websocket-mode'.")

(defvar hass-websocket-connected-hook #'hass-websocket--subscribe-to-state-changes
"Hook called after successful authentication to websocket.")
"Hook called after successful authentication to websocket.")

;; Internal state
(defvar hass-websocket--connection '()
Expand All @@ -79,8 +83,8 @@
(type (cdr (assoc 'type content))))
(cond ((string= "auth_required" type)
(hass-websocket--send
`((type . "auth")
(access_token . ,(hass--apikey)))))
`((type . "auth")
(access_token . ,(hass--apikey)))))
((string= type "auth_ok")
(message "hass: Connected to websocket")
(run-hooks 'hass-websocket-connected-hook))
Expand Down Expand Up @@ -110,8 +114,8 @@ Assistant."
(let ((entity-id (cdr (assoc 'entity_id data))))
(when (member entity-id hass-tracked-entities)
(hass--query-entity-result
entity-id
(cdr (assoc 'state (cdr (assoc 'new_state data))))))))
entity-id
(cdr (assoc 'state (cdr (assoc 'new_state data))))))))

;; Requests - Send to Home Assistant over websocket
(defun hass-websocket--subscribe-to-state-changes ()
Expand All @@ -135,13 +139,13 @@ MESSAGE is an alist to be encoded into a JSON object."
(defun hass-websocket--connect ()
"Establish a websocket connection to Home Assistant."
(setq hass-websocket--connection
(websocket-open (format "%s://%s:%s/api/websocket"
(if hass-insecure "ws" "wss")
hass-host
hass-port)
:on-message #'hass-websocket--handle-message
:on-open (lambda (_websocket) (setq hass-websocket--interactions 0))
:on-close (lambda (_websocket) (setq hass-websocket--connection nil)))))
(websocket-open (format "%s://%s:%s/api/websocket"
(if hass-insecure "ws" "wss")
hass-host
hass-port)
:on-message #'hass-websocket--handle-message
:on-open (lambda (_websocket) (setq hass-websocket--interactions 0))
:on-close (lambda (_websocket) (setq hass-websocket--connection nil)))))

(defun hass-websocket--disconnect ()
"Disconnect the websocket connection to Home Assistant."
Expand Down
Loading

0 comments on commit c6d9bd2

Please sign in to comment.