top of page

The Home Assistant Shortcut Keys Caper

  • Giles
  • 16 minutes ago
  • 6 min read

I've always been enthralled by the idea of remotely controlling things, so Home Assistant is a gift from the heavens for a tinkerer like me. I've got as many devices as possible around the house hooked up to it. Including, of course, lights in most rooms. Via Philips Hue, Shelly or Sonoff switches, or homebrew boxes using ESPHome or anything else, it doesn't matter - Home Assistant is my go-to single point of control.


In the home office I've got a variety of lights. There's the main light, task lighting on the desk, backlights behind the monitors and even a light that's in-my-face for use on video calls. Home Assistant controls them all of course. But, it's a bit of a pain to have to reach for the Home Assistant interface to change scene, to turn the desk lights on, or engage the "Zoom Light".


I could get Alexa to do it. Sure, this works fine, I don't have to reach for the Home Assistant app or web page so that's an improvement. But still, I have to think about words and actually say them out loud. This is also too much effort.


So I thought: I sit in front of the computer for work, and, to be honest, for play, relaxation, and hobbies as well. My hands are very rarely not resting on the computer keyboard. The keyboard has lots of buttons, so why not just program a few of these to switch the lights? Should be simple, right?


Remotely Controlling Home Assistant

There are loads of ways I could do this, but the easiest one I could think of was to leverage my existing MQTT installation. Home Assistant and MQTT go hand-in-hand, with the MQTT integration offering device auto discovery and Automation triggers out of the box. Also, I've written my own Home Assistant integrations using MQTT as the middleware, so it's safe to say I know it well. But an in-depth knowledge of MQTT is not a prerequisite to today's challenge.


In a nutshell, MQTT is a lightweight message exchange system. Messages are published to topics, which are simply text strings with separators to allow for grouping of common devices or functions. The message can optionally contain a payload.


Home Assistant's MQTT Integration provides a trigger to which Automations can be attached. You simply tell the Automation what MQTT topic to watch for, and what to do when it gets a message that matches. For a simple "do this" switch command, this is ideal, and doesn't need any more complicated interaction with Home Assistant.


MQTT-Triggered Automation

I set up the following Automation to toggle the Zoom light when a trigger MQTT message is received. I set it up to match on the word "toggle" being the message payload, although this probably wasn't necessary.


The MQTT topic 'tred51/edict/remotes/desktop/zoomlight' was essentially invented, though I have a standard hierarchy I use for my homebrew automations, so I based it off that.

alias: "Remote: Zoom Light"
description: ""
triggers:
  - trigger: mqtt
    topic: tred51/edict/remotes/desktop/zoomlight
    payload: toggle
conditions: []
actions:
  - action: switch.toggle
    metadata: {}
    data: {}
    target:
      entity_id: switch.sw21
mode: single

Making MQTT

Now I have Home Assistant listening for a magic MQTT message, I need to generate and send such a missive.


The Mosquitto project publishes, amongst other MQTT goodies, a set of command-line programs for simple use, the one we're interested here is mosquitto_pub.exe which allows for the sending ("publication") of MQTT messages. For one-shot message sending this feels optimal; there's no need to write my own application or anything. I installed the tools from here.


Sending a message is as simple as calling the program on the command line and passing it the parameters as follows:

> mosquitto_pub.exe -h my.mqtt.server.com -u username -p password \
	-t 'tred51/edict/remotes/desktop/zoomlight' -m 'toggle'

I ran this and - PRESTO! Home Assistant toggles my Zoom light.


Shortcut Keys

However much it would make me feel like a hacker, I don't want to type a command line into the computer whenever I want to change the lighting state. The goal of this endeavour, after all, is to have keyboard shortcuts to do the work for me.


Unfortunately, and for a reason I just can't fathom, Windows doesn't have built-in user-customisable keyboard shortcuts, at least, not that I was able to find quickly enough when I was rattling through this project. Instead there are various little apps people have written to add such functionality to Windows. So, let's pick one.


AutoHotKey

One such program is AutoHotKey. This is an expansive, hugely powerful way of scripting things in Windows. It offers a trillion times the flexibility I need for this project and therefore was the totally obvious choice (I need saving from myself sometimes.)


You install it, then start its agent against a predefined script file. The agent runs in the background (showing up under a system tray icon) running its script, which can run in a loop or - usefully here - respond to key combinations. You can run multiple agents with multiple script files, or you can put more than one function in a single script file. It's completely customisable and can be made to work in whatever way makes most sense.


Writing AutoHotKey scripts is an art unto itself and there is a vast trove of reference. I only wanted a simple shortcut key to run the mosquitto_pub command, so the following extremely simple script is all I needed:

;Requires AutoHotkey v2.0
MqttApp := "C:\Program Files\mosquitto\mosquitto_pub.exe"
MqttHost := "my.mqtt.server.com"
MqttUser := "username"
MqttPass := "password"

MqttFunction := MqttApp " -h " MqttHost " -u " MqttUser " -P " MqttPass " -t tred51/edict/remotes/desktop/"

^!Numpad0::
{
	Run MqttFunction 'zoomlight -m toggle',, "Hide"
}

  • It uses some variables and a bit of string concatenation to keep things neat.

  • The ^!Numpad0 bit is doing all the hard work of defining the shortcut hotkey combo - I chose Ctrl (^) Alt (!) Numpad-0 (see here for the full reference on hotkeys)

  • The "Hide" directive on the end of the Run command stops AutoHotKey popping up a command prompt window when it triggers the command.

  • I could, I think, lose the curly brackets, because I'm only running a single command in the hotkey. But I think it makes the script file easier to read with them in, so they've remained.


I saved the script file to Documents and popped a shortcut to the AutoHotKey agent in shell:startup to start it at log-in:

# Target to pop into a shell:startup shortcut
C:\Users\giles\AppData\Local\Programs\AutoHotkey\v2\AutoHotkey64.exe "C:\Users\giles\Documents\AutoHotkey\HA Keys.ahk"

Results

This all "just worked". Now whenever I hit the key combination Ctrl-Alt-Numpad0, the Zoom light toggles on/off!


All that was then needed was to repeat the installation of Mosquitto and AutoHotKey on the other PCs I have in my office (which are all Windows), and copy the same AutoHotKey script across, so all PCs share the same shortcut keys and it doesn't matter which one has my keyboard focus.


Cross-platforming

Mosquitto clients are available for all operating systems so it'd be straightforward to do this cross-platform too - indeed, one would hope other platforms might allow users to define custom shortcut commands without the need to employ a third party utility like AutoHotKey.


Going Further

I also wanted to have shortcut keys for the main study lighting looks.


I created a new Automation in Home Assistant using a different MQTT trigger topic. This time I check the payload and execute a different scene depending on what's found:

alias: "Remote: Study Main Light"
description: ""
triggers:
  - trigger: mqtt
    topic: tred51/edict/remotes/desktop/mainlight
conditions: []
actions:
  - choose:
      - conditions:
          - condition: template
            value_template: "{{ 'off' == trigger.payload }}"
        sequence:
          - action: scene.turn_on
            metadata: {}
            data: {}
            target:
              entity_id: scene.study_off
      - conditions:
          - condition: template
            value_template: "{{ 'bright' == trigger.payload }}"
        sequence:
          - action: scene.turn_on
            metadata: {}
            data: {}
            target:
              entity_id: scene.study_study_bright
      - conditions:
          - condition: template
            value_template: "{{ 'pinky' == trigger.payload }}"
        sequence:
          - action: scene.turn_on
            metadata: {}
            data: {}
            target:
              entity_id: scene.study_study_warm
      - conditions:
          - condition: template
            value_template: "{{ 'evening' == trigger.payload }}"
        sequence:
          - action: scene.turn_on
            metadata: {}
            data: {}
            target:
              entity_id: scene.study_study_late
mode: single

Then, I added further shortcut keys to the AutoHotKey script:

;Requires AutoHotkey v2.0
MqttApp := "C:\Program Files\mosquitto\mosquitto_pub.exe"
MqttHost := "my.mqtt.server.com"
MqttUser := "username"
MqttPass := "password"

MqttFunction := MqttApp " -h " MqttHost " -u " MqttUser " -P " MqttPass " -t tred51/edict/remotes/desktop/"

^!Numpad0::
{
	Run MqttFunction 'zoomlight -m toggle',, "Hide"
}
^!Numpad1::
{
	Run MqttFunction 'mainlight -m bright',, "Hide"
}
^!Numpad2::
{
	Run MqttFunction 'mainlight -m pinky',, "Hide"
}
^!Numpad3::
{
	Run MqttFunction 'mainlight -m off',, "Hide"
}
^!Numpad4::
{
	Run MqttFunction 'mainlight -m evening',, "Hide"
}

All I have to do now is to remember which button does what! But, by using the exact same AutoHotKey script on all computers, the muscle memory quickly forms.


Conclusion

This was a fun little project which delivers me a useful control vector to my Home Assistant-controlled lights without even needing to lift my hands off the keyboard, or speak to Alexa.


Follow me:
  • Mastodon
  • Twitter
  • Youtube
  • flickr

 Technical Capers © Giles Moss 2023

bottom of page