hass icon indicating copy to clipboard operation
hass copied to clipboard

An Emacs package for interacting with Home Assistant

#+TITLE: hass

[[https://melpa.org/#/hass][file:https://melpa.org/packages/hass-badge.svg]] [[https://stable.melpa.org/#/hass][file:https://stable.melpa.org/packages/hass-badge.svg]]

#+HTML:

~hass~ is an Emacs package that enables integration with [[https://www.home-assistant.io/][Home Assistant]]. Call Home Assistant services, hook into Home Assistant events, and create convenient dashboards!

  • Contents :PROPERTIES: :TOC: :include all :force ((nothing)) :ignore (this) :local (nothing) :END: :CONTENTS:
  • [[#installation][Installation]]
    • [[#use-package][use-package]]
    • [[#straightel][straight.el]]
    • [[#doom-emacs][Doom Emacs]]
  • [[#version-3-changes][Version 3 changes]]
    • [[#brand-new-dashboards][Brand new dashboards!]]
    • [[#hass-dash-layout-is-now-hass-dash-layouts][hass-dash-layout is now hass-dash-layouts]]
    • [[#hass-setup-no-longer-required][hass-setup no longer required.]]
    • [[#removed-deprecated-name-widget-property][Removed deprecated :name widget property.]]
    • [[#removed-polling-mode][Removed polling-mode]]
  • [[#configuration][Configuration]]
    • [[#getting-an-api-key][Getting an API Key]]
    • [[#dashboard-layout][Dashboard layout]]
      • [[#full-example][Full example]]
      • [[#structure][Structure]]
      • [[#widgets][Widgets]]
        • [[#state][State]]
        • [[#button][Button]]
        • [[#toggle][Toggle]]
  • [[#usage][Usage]]
    • [[#dashboard][Dashboard]]
    • [[#payloads][Payloads]]
    • [[#hooks][Hooks]]
  • [[#license][License]] :END:

[[file:images/screenshot1.png]]

  • Installation This package is available on [[https://melpa.org/][MELPA]].

** use-package

#+BEGIN_SRC emacs-lisp :results none (use-package hass :ensure t :init ;; -- Configuration goes here -- ) #+END_SRC

** straight.el

#+BEGIN_SRC emacs-lisp :results none (straight-use-package 'hass) ;; -- Configuration goes here -- #+END_SRC

** Doom Emacs

Place in your ~packages.el~ then run ~doom sync~ to pull the repository:

#+BEGIN_SRC emacs-lisp :results none (package! hass) #+END_SRC

Then load the package in your main config file.

#+BEGIN_SRC emacs-lisp :results none (use-package! hass :init ;; -- Configuration goes here -- ) #+END_SRC

  • Version 3 changes ** Brand new dashboards! Thanks to [[https://github.com/cprussin][cprussin]], the dashboards have been completely overhauled. They're much more flexible by more cleanly utilizing the built-in widget framework. Additionally, you can have multiple dashboards configured.

** =hass-dash-layout= is now =hass-dash-layouts=

** hass-setup no longer required. Calling ~hass-setup~ is no longer required. It has been renamed ~hass-ensure~. It can still be useful if you want to 'ensure' an API connection has already been established and entity information is already up to date before you open a dashboard.

** Removed deprecated :name widget property.

** Removed polling-mode Didn't seem useful.

  • Configuration

Both ~hass-host~ and ~hass-apikey~ must be set to use this package. Set ~hass-host~ to the hostname or IP of Home Assistant instance. If you are not using SSL/TLS to connect to your Home Assistance instance, set ~hass-insecure~ to ~t~. If you are using a port number other than the default =8123=, specify the port number with ~hass-port~.

#+BEGIN_SRC emacs-lisp :results none (setq hass-host "homeassistant") (setq hass-apikey "APIKEY-GOES-IN-HERE") (setq hass-port 8123) #+END_SRC

Alternatively, you can store a function inside ~hass-apikey~. This will be executed on every query. In turn, this approach requires the token to be stored in your gpg store e.g. =~/.password-store/emacs-apikey.gpg=

#+BEGIN_SRC emacs-lisp :results none (setq hass-host "homeassistant") (setq hass-apikey (lambda () (auth-source-pass-get 'secret "emacs-apikey"))) #+END_SRC

Once those variables are set, you can call ~(hass-ensure)~ to query the Home Assistance instance and populate available entities and services. Otherwise, this will be done when it is needed. ** Getting an API Key

Ensure that your Home Assistant instance is configured to support API calls by following the instructions [[https://www.home-assistant.io/integrations/api/][here]].

Retrieve your API key a.k.a. /Long-Lived Access Token/ by logging into your Home Assistant instance and going to your profile by selecting your username in the lower-left corner or going to this URL: =http://HOME-ASSISTANT-URL:8123/profile=. You can generate an API token at the very bottom of this page.

** Dashboard layout

*** Full example #+BEGIN_SRC emacs-lisp :results none (setq hass-dash-layouts '((default . ; Key for dashboard. Shows up with completing-read when calling `hass-dash-open'. ((hass-dash-group ; Create a widget group. :title "Home Assistant" ; Give the group a title at the top. :format "%t\n\n%v" ; %t is where the title goes and %v is the widget it owns. (hass-dash-group ; Create a subgroup of widgets. :title "Kitchen" :title-face outline-2 ; Give it a unique face to make it stand out. (hass-dash-toggle :entity-id "light.kitchen_lights") (hass-dash-toggle :entity-id "light.master_bedroom_lights") (hass-dash-toggle :entity-id "switch.entry_light" :label "Hallway" ; Override the widgets friendly name :confirm t))) ; Require a y/n confirmation when toggling this entity. (hass-dash-group :title "Group 2" :format "\n\n%t\n\n%v" (hass-dash-toggle :entity-id "light.master_bedroom_fan_light"))))

    (simple . ; Declaring a top-level group is optional and implied.
            ((hass-dash-toggle :entity-id "light.kitchen_lights")
             (hass-dash-toggle :entity-id "switch.entry_lights")))))

#+END_SRC

*** Structure To use the dashboard feature, ~hass-dash-layouts~ must be configure to tell ~hass~ what the layout should look like. The layout is constructed with three components: groups, widgets, and properties.

  • Each element in ~hass-dash-layouts~ is a dashboard.
  • A dashboard is a cons of its key/id and the widgets it contains.
  • A widget is an Emacs widget, probably one from this package.

Any widgets defined in ~hass-dash-layouts~ are automatically inserted into the ~hass-tracked-entities~ list in order to receive state updates.

*** Widgets

All widgets contain at least the following properties:

| Widget Property | Description | |---------------------+----------------------------------------------------------------------------------------------| | ~: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 prefixed to the widget. | | ~:confirm~ | When ~t~ or a string, ask for confirmation before calling the service. |

**** State

A 'state' widget is a read-only widget to simply display the state of some entity. I typically like to use this as the very first widget in a group to show the overall status of the group. For example, a vacuum:

#+BEGIN_SRC emacs-lisp (hass-dash-group :title "Vacuum" (hass-dash-state :entity-id "vacuum.valetudo_vacuum" :format "%v\n") ; Vacuum related widgets ; ... ) #+END_SRC

**** Button

A 'button' widget is a push-button widget to call a service.

#+BEGIN_SRC emacs-lisp (hass-dash-button :entity-id "vacuum.valetudo_vacuum" :service "vacuum.start" :format "%[%t: %v%]\n" :label "Clean") #+END_SRC

**** Toggle

A 'toggle' widget is similar to a button, except it'll only show on or off. If the state is anything other than "on", then it will show "off".

#+BEGIN_SRC emacs-lisp (hass-dash-toggle :entity-id "light.kitchen") #+END_SRC

  • Usage

To call a service on Home Assistant, use the ~hass-call-service~ function which has two required arguments: ~entity-id~ and ~service~.

#+BEGIN_SRC emacs-lisp :results none (hass-call-service "switch.bedroom_light" "switch.toggle") #+END_SRC

If you call ~hass-call-service~ interactively, it will prompt you for an entity ID and then the respective service you want to call.

** Dashboard

After configuring the ~hass-dash-layouts~, use the function ~hass-dash-open~ to pop open a dashboard. This can be enhanced with standard buffer management configuration or packages like =popper= and/or =shackle=.

** Payloads

For services that require additional data use the ~hass-call-service-with-payload~ function. The second argument, ~payload~, requires an JSON encoded string.

This example publishes to an MQTT topic:

#+BEGIN_SRC emacs-lisp :results none (hass-call-service-with-payload "mqtt.publish" (json-encode '(("payload" . "PERFORM") ("topic" . "valetudo/vacuum/LocateCapability/locate/set")))) #+END_SRC

You could pass a JSON string directly, but that would require escaping every quote which can be cumbersome. Here's what the encoded list above looks like in JSON:

#+BEGIN_SRC javascript { "payload": "PERFORM", "topic": "valetudo/vacuum/LocateCapability/locate/set" } #+END_SRC

** Hooks

The most useful hook is a function list named ~hass-entity-state-changed-functions~. Functions in this list are passed a single argument ~entity-id~ which is the entity id of the entity whose state has changed since it was last updated. Using this function hook along side [[*Tracking entities][tracking entities]] enables Emacs to react to changes to Home Assistant entities.

This example will display the state of an entity when it changes:

#+BEGIN_SRC emacs-lisp :results none (add-hook 'hass-entity-state-changed-functions (lambda (entity-id) (message "The entity %s state has changed to %s." entity-id (hass-state-of entity-id)))) #+END_SRC

The other two hooks available are ~hass-entity-updated-hook~ and ~hass-service-called-hook~. ~hass-entity-updated-hook~ is called when the state of an entity is updated, regardless of if it changed or not. ~hass-service-called-hook~ is called when a service is called.

#+BEGIN_SRC emacs-lisp :results none (add-hook 'hass-service-called-hook (lambda () (message "A service was called."))) (add-hook 'hass-entity-updated-hook (lambda () (message "An entitys' state was updated."))) #+END_SRC

  • License

MIT