Macbook sensor via REST API


#1

Hi all
I would like a sensor displaying when I am using my Macbook, not just when it is on wifi. I recall seeing on the forum or twitter that there is an app for the Mac which creates a restful API which allows you to monitor the state of your mac remotely via the API. I could then use a RESTful sensor.
Does anybody know the details?
Cheers


#2

OK found it, but need to figure out how to configure it.!


#3

OK, so it is an API for controlling the Mac. Still useful, but not what I need.


#4

Try SleepWatcher. I use it to trigger scripts when wake/sleep, but it has also options to trigger on idleness.


#5

i’m using two python scripts on my macbook that set an mqtt topic for a sleep sensor. i used to call the scripts with scenario, which didn’t work as reliably as i wished. i also tried sleepwatcher, but moved away from that because of lacking support for newer operating systems.

i’m getting pretty reliable results with hammerspoon now, calling the scripts when the mac awakes or goes to sleep.

wake.py

#!/usr/bin/env python3
import paho.mqtt.publish as publish
import sys
import ssl
import requests
import time

def wait_for_internet_connection(url='https://[HA-hostname]:[PORT]/local/custom_ui/floorplan/floorplan.css', timeout=5):
    for x in range(0, 15):
        try:
            _ = requests.get(url, timeout=timeout)
            return True
        except requests.ConnectionError:
            time.sleep(2)
            pass
    return False

def publish_message(topic, message):
    print("Publishing to MQTT topic: " + topic)
    print("Message: " + message)
    auth = {
      'username' : 'xxx',
      'password' : 'xxx'
    }
    tls = {
      'ca_certs':"/usr/local/etc/openssl/cert.pem",
      'tls_version':ssl.PROTOCOL_TLSv1
    }
    publish.single(topic, message, hostname="[mqtt-hostname]", port=[PORT], auth=auth, tls=tls, retain=True)

if __name__ == '__main__':
    if wait_for_internet_connection():
        publish_message("ixbook/sleep/status", "stop")
    else:
        print("no connection")

sleep.py

#!/usr/bin/env python3
import paho.mqtt.publish as publish
import sys
import ssl
import requests
import time

def wait_for_internet_connection(url='https://[HA-hostname]:[PORT]/local/custom_ui/floorplan/floorplan.css', timeout=5):
    for x in range(0, 15):
        try:
            _ = requests.get(url, timeout=timeout)
            return True
        except requests.ConnectionError:
            time.sleep(2)
            pass
    return False

def publish_message(topic, message):
    print("Publishing to MQTT topic: " + topic)
    print("Message: " + message)
    auth = {
      'username' : 'xxx',
      'password' : 'xxx'
    }
    tls = {
      'ca_certs':"/usr/local/etc/openssl/cert.pem",
      'tls_version':ssl.PROTOCOL_TLSv1
    }
    publish.single(topic, message, hostname="mqtt-hostname", port=[PORT], auth=auth, tls=tls, retain=True)

if __name__ == '__main__':
    if wait_for_internet_connection():
        publish_message("ixbook/sleep/status", "start")
    else:
        print("no connection")

this is the module that i use to call the scripts (embedded in a .sh file to call other services, too) with the hammerspoon caffeinate.watcher function:

sleep_watcher.lua

local sleepWatcher = require 'hs.caffeinate.watcher'

local function sleepWatch(eventType)
	if (eventType == hs.caffeinate.watcher.systemWillSleep) then
		hs.notify.show("Bye!", "", "")
		hs.execute('/Users/ix/Library/Application\\ Support/LaunchBar/Actions/sleep.lbaction/Contents/Scripts/sleep.sh', true)
	elseif (eventType == hs.caffeinate.watcher.systemDidWake) then
		hs.notify.show("Wake sequence initiated!", "", "")
		hs.execute('/Users/ix/Library/Application\\ Support/LaunchBar/Actions/wake.lbaction/Contents/Scripts/wake.sh', true)
    end
end

return {
    init = function()
        sleepWatcher.new(sleepWatch):start()
    end
}

if your mqtt broker is accessible from outside your network, this works anywhere, if the mac has an internet connection. it retries serveral times to connect to the net, to give the mac time after wakeup to connect to the network.

hammerspoon has a steep learning curve, but is a very usefull tool, that i don’t want to miss any more. something like this should also work for setting mqtt topics for your screen status or any other mac status or telemetry.


#6

Thanks for your advice guys. @diplix RE hammerspoon (which looks awesome btw), just a comment, but I assume you could avoid the python scripts entirely by using the http post command? I appreciate that in your case you are reusing those python scripts elsewhere.
Cheers!


#7

yes, those scripts i use could very much be optimized or be worked around with a single api-call. i tend to overcomplicate things often, but am fine with it as long as i get it to work easily.


#8

OK I edited sleep_watcher.lua to be

local sleepWatcher = require 'hs.caffeinate.watcher'
headers = {'content-type': 'application/json'}
data = {'entity_id': 'switch.macbook_power'}

local function sleepWatch(eventType)
	if (eventType == hs.caffeinate.watcher.systemWillSleep) then
		hs.notify.show("Bye!", "", "")
		hs.http.post('http://192.168.0.XX:8123/api/services/switch/turn_off', data, headers)
	elseif (eventType == hs.caffeinate.watcher.systemDidWake) then
		hs.notify.show("Wake sequence initiated!", "", "")
		hs.http.post('http://192.168.0.XX:8123/api/services/switch/turn_off', data, headers)
    end
end

return {
    init = function()
        sleepWatcher.new(sleepWatch):start()
    end
}

To I have to add sleep_watcher.lua in a particular folder within .hammerspoon, or do any config, for this script to be active? Scowering docs for this info…
Cheers


#9

this is my hammerspoon config (init.lua):

local alert = require 'hs.alert'

import = require('utils/import')
import.clear_cache()

config = import('config')

function config:get(key_path, default)
    local root = self
    for part in string.gmatch(key_path, "[^\\.]+") do
        root = root[part]
        if root == nil then
            return default
        end
    end
    return root
end

local modules = {}

for _, v in ipairs(config.modules) do
    local module_name = 'modules/' .. v
    local module = import(module_name)

    if type(module.init) == "function" then
        module.init()
    end

    table.insert(modules, module)
end

local buf = {}

if hs.wasLoaded == nil then
    hs.wasLoaded = true
    table.insert(buf, "Hammerspoon loaded: ")
else
    table.insert(buf, "Hammerspoon re-loaded: ")
end

table.insert(buf, #modules .. " modules.")

alert.show(table.concat(buf))

the sleep_watcher.lua (and some other modules) are in a folder called modules in the same directory as init.lua and get initialed by the init script. (i just copy, pasted and changed this from someones else’s config, AFAIK this:


#10

Thanks, can you tell me which other files you have in .hammerspoon ?
I am getting errors on missing imports.
Cheers


#11

actually all of them, see screenshot — and sorry, this was the repo i used for my config:

unfortunatly i don’t remember exactly how i configured it or how i disabled certain modules, it’s already two month ago …


#12

OK this is working now. I have in init.lua:

data = '{"entity_id": "switch.macbook_power"}'

headers = {}
headers["x-ha-access"] = "your_password"


local sleepWatcher = hs.caffeinate.watcher.new(function (eventType)
      if (eventType == hs.caffeinate.watcher.systemDidWake) then
          hs.notify.show("Wake sequence initiated!", "", "")
     		  hs.http.post("http://192.168.0.100:8123/api/services/switch/turn_on", data, headers)
      elseif (eventType == hs.caffeinate.watcher.systemWillSleep) then
            hs.notify.show("Bye!", "", "")
     		    hs.http.post("http://192.168.0.100:8123/api/services/switch/turn_off", data, headers)
      end
end)
sleepWatcher:start()

#13

I just stumbled across this thread and this looks really cool.
I’ve been looking for a decent, reliable solution to detect the state of my macbook for a long time.

Could you share the corresponding HA configuration for the macbook_power switch?
I have never used template switches before and I’m not really sure how to configure the value_template option in this case.

Sebastian


#14

Sure checkout:


#15

Thanks!

Sebastian


#16

Hey this is a great. Is there anyway to show the battery level. I would like to set up an automation that turns on and off a switch/plug for my power on low and high battery. Essentially if I charge my MacBook and forget to unplug it. When the battery is charged the switch will turn off.


#17

Hammerspoon is very powerful so I’m sure that’s possible :+1:


#18

I looked into it and saw that there is an API for battery. Not sure how to implement it though. I’ll take a look when I get to my Mac. I am sure with your code I could just add on. Thanks


#19

I’m interested in monitoring CPU temps and fans so I can trigger lights etc (hackintosh/room lights).

Is this beyond the scope of what’s already been discussed?

Thanks.


#20

I’m sure that’s possible with Hammerspoon, but haven’t investigated myself