Support for Home Assistant API (no MQTT needed) Experimental feature (#160)

This commit is contained in:
Masoko
2024-12-01 16:23:55 +02:00
committed by GitHub
parent 04909912d3
commit be4b2a2c07
6 changed files with 194 additions and 75 deletions

View File

@@ -41,6 +41,7 @@ The easiest way to track your Raspberry Pi or Ubuntu computer system health and
## What is new
* 2024-12-01: Support for Home Assistant API (no MQTT needed)
* 2024-11-06: External sensors by @pallago
* 2024-10-25: Apt updates sensor
* 2024-10-24: Added rpi_power_status sensor
@@ -61,15 +62,19 @@ The easiest way to track your Raspberry Pi or Ubuntu computer system health and
## CLI arguments
```
usage: rpi-mqtt-monitor [-h] [--display] [--service] [--version] [--update]
usage: rpi-mqtt-monitor [-h] [-H] [-d] [-s] [-v] [-u] [-w]
Monitor CPU load, temperature, frequency, free space, etc., and publish the data to an MQTT server or Home Assistant API.
options:
-h, --help show this help message and exit
--display, -d display values on screen
--service, -s run script as a service
--version, -v display version
--update, -u update script and config
--hass, -H display Home assistant wake on lan configuration
-h, --help show this help message and exit
-H, --hass_api send readings via Home Assistant API (not via MQTT)
-d, --display display values on screen
-s, --service run script as a service, sleep interval is configurable in config.py
-v, --version display installed version and exit
-u, --update update script and config then exit
-w, --hass_wake display Home assistant wake on lan configuration
```
@@ -88,10 +93,11 @@ Raspberry Pi MQTT monitor will be installed in the location where the installer
The auto-installer needs the software below and will install it if its not found:
* git
* python (2 or 3)
* python-pip
* git
* paho-mqtt
* paho-mqtt (python module)
* requests (python module)
Only python is not automatically installed, the rest of the dependencies should be handled by the auto installation.
It will also help you configure the host and credentials for the mqtt server in config.py and create the service or cronjob configuration for you.
@@ -104,14 +110,14 @@ It is recommended to run the script as a service, this way you can use the resta
If you are using discovery_messages, then this step is not required as a new MQTT device will be automatically created in Home Assistant and all you need to do is add it to a dashboard.
Use '''python3 src/rpi-cpu2mqtt.py --hass''' to display the configuration for Home Assistant wake on lan switch.
Use '''rpi-mqtt-monitor --hass_wake''' to display the configuration for Home Assistant wake on lan switch.
[moved to wiki](../../wiki/Home-Assistant-Integration-(outdated))
## To Do
- fix uptime sensor to use timestamp
- integrate hass api
- improve hass api integration
## Feature request

View File

@@ -1,3 +1,4 @@
#!/bin/bash
find_python(){
if [[ $(python3 --version) ]]; then
python=$(which python3)
@@ -78,21 +79,7 @@ install_requirements(){
deactivate
}
update_config(){
if [ -f src/config.py ]; then
read -p "src/config.py already exists Do you want to remove it? (y/n) " yn
case $yn in
[Yy]* ) echo "replacing config file";;
[Nn]* ) return;;
* ) echo "Please answer y for yes or n for no.";;
esac
fi
user=$(whoami)
sed -i "s/os_user_to_be_replaced/${user}/" src/config.py
print_green "+ Copy config.py.example to config.py"
cp src/config.py.example src/config.py
mqtt_configuration(){
printm "MQTT settings"
printf "Enter mqtt_host: "
@@ -126,6 +113,59 @@ update_config(){
if [[ "$CONTROL" =~ ^([yY][eE][sS]|[yY])$ ]]; then
sed -i "s/display_control = False/display_control = True/g" src/config.py
fi
finish_message="MQTT broker"
}
hass_api_configuration(){
printf "Enter Home Assistant API URL (defalut is http://localhost:8123): "
read HA_URL
if [ -z "$HA_URL" ]; then
HA_URL="http://localhost:8123"
fi
sed -i "s|your_hass_host|${HA_URL}|" src/config.py
printf "Enter Home Assistant API Token: "
read HA_TOKEN
sed -i "s|your_hass_token|${HA_TOKEN}|" src/config.py
hass_api=" --hass_api"
finish_message="Home Assistant API"
}
update_config(){
if [ -f src/config.py ]; then
read -p "src/config.py already exists Do you want to remove it? (y/n) " yn
case $yn in
[Yy]* ) echo "replacing config file";;
[Nn]* ) return;;
* ) echo "Please answer y for yes or n for no.";;
esac
fi
print_green "+ Copy config.py.example to config.py"
cp src/config.py.example src/config.py
user=$(whoami)
sed -i "s/os_user_to_be_replaced/${user}/" src/config.py
echo "Do you want to use Home Assistant API or MQTT?"
echo "1) Home Assistant API"
echo "2) MQTT (default)"
read -p "Enter your choice [1 or 2]: " choice
# Run the appropriate configuration function based on the user's choice
case $choice in
1)
hass_api_configuration
;;
2 | "")
mqtt_configuration
;;
*)
echo "Invalid choice. Defaulting to MQTT configuration."
mqtt_configuration
;;
esac
print_green "+ config.py is updated with provided settings"
@@ -152,8 +192,8 @@ set_cron(){
MIN=2
fi
echo "Adding the line below to your crontab"
echo "*/${MIN} * * * * cd ${cwd}; ${python} ${cwd}/src/rpi-cpu2mqtt.py"
echo "*/${MIN} * * * * cd ${cwd}; ${python} ${cwd}/src/rpi-cpu2mqtt.py" >> tempcron
echo "*/${MIN} * * * * cd ${cwd}; ${python} ${cwd}/src/rpi-cpu2mqtt.py${hass_api}"
echo "*/${MIN} * * * * cd ${cwd}; ${python} ${cwd}/src/rpi-cpu2mqtt.py${hass_api}" >> tempcron
crontab tempcron
fi
rm tempcron
@@ -178,7 +218,7 @@ set_service(){
sed -i "s/120/${MIN}/" src/config.py
cwd=$(pwd)
user=$(whoami)
exec_start="${python} ${cwd}/src/rpi-cpu2mqtt.py --service"
exec_start="${python} ${cwd}/src/rpi-cpu2mqtt.py --service${hass_api}"
print_green "+ Copy rpi-mqtt-monitor.service to /etc/systemd/system/"
sudo cp ${cwd}/rpi-mqtt-monitor.service /etc/systemd/system/
sudo sed -i "s|WorkingDirectory=.*|WorkingDirectory=${cwd}|" /etc/systemd/system/rpi-mqtt-monitor.service
@@ -221,7 +261,7 @@ main(){
done
printm "Installation is complete."
echo "rpi-mqtt-monitor is now running and sending information to your MQTT broker."
echo "rpi-mqtt-monitor is now running and sending information to your ${finish_message}."
echo "To see all available options run: rpi-mqtt-monitor -h in the terminal."
}

View File

@@ -1,4 +1,6 @@
#!/bin/bash
# Description: Remote Installation script for rpi-mqtt-monitor
printm(){
length=$(expr length "$1")
length=$(($length + 4))

View File

@@ -1 +1,2 @@
paho-mqtt==1.6.1
requests

View File

@@ -10,6 +10,10 @@ mqtt_port = "1883"
mqtt_discovery_prefix = "homeassistant"
mqtt_topic_prefix = "rpi-MQTT-monitor"
# Below hass_ configuration is only needed if you use Home Assistant API instead of
hass_token = "your_hass_token"
hass_host = "your_hass_host"
# Messages configuration
# Uncomment the line bellow to send just one CSV message containing all values (this method don't support HA discovery_messages)

View File

@@ -20,6 +20,7 @@ import re
import html
import uuid
import glob
import requests
#import external sensor lib only if one uses external sensors
if config.ext_sensors:
# append folder ext_sensor_lib
@@ -166,8 +167,9 @@ def check_cpu_temp():
def check_sys_clock_speed():
full_cmd = "awk '{printf (\"%0.0f\",$1/1000); }' </sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq"
return subprocess.Popen(full_cmd, shell=True, stdout=subprocess.PIPE).communicate()[0]
byte_data = subprocess.Popen(full_cmd, shell=True, stdout=subprocess.PIPE).communicate()[0]
sys_clock_speed = int(byte_data.decode("utf-8").strip())
return sys_clock_speed
def check_uptime(format):
@@ -392,7 +394,7 @@ def get_release_notes(version):
return release_notes
def config_json(what_config, device="0"):
def config_json(what_config, device="0", hass_api=False):
model_name = check_model_name()
manufacturer = get_manufacturer()
os = get_os()
@@ -416,18 +418,18 @@ def config_json(what_config, device="0"):
data["state_topic"] = config.mqtt_topic_prefix + "/" + hostname + "/" + what_config
data["unique_id"] = hostname + "_" + what_config
if what_config == "cpuload":
if what_config == "cpu_load":
data["icon"] = "mdi:speedometer"
data["name"] = "CPU Usage"
data["state_class"] = "measurement"
data["unit_of_measurement"] = "%"
elif what_config == "cputemp":
elif what_config == "cpu_temp":
data["icon"] = "hass:thermometer"
data["name"] = "CPU Temperature"
data["unit_of_measurement"] = "°C"
data["device_class"] = "temperature"
data["state_class"] = "measurement"
elif what_config == "diskusage":
elif what_config == "used_space":
data["icon"] = "mdi:harddisk"
data["name"] = "Disk Usage"
data["unit_of_measurement"] = "%"
@@ -573,6 +575,20 @@ def config_json(what_config, device="0"):
else:
return ""
# Return our built discovery config
if hass_api:
result = {
"name": data["name"],
"icon": data["icon"],
"state_class": data["state_class"],
"unit_of_measurement": data["unit_of_measurement"]
}
if "device_class" in data:
result["device_class"] = data["device_class"]
if "unique_id" in data:
result["unique_id"] = data["unique_id"]
return result
return json.dumps(data)
@@ -635,6 +651,44 @@ def publish_update_status_to_mqtt(git_update, apt_updates):
client.disconnect()
def publish_to_hass_api(cpu_load=0, cpu_temp=0, used_space=0, voltage=0, sys_clock_speed=0, swap=0, memory=0,
uptime_days=0, uptime_seconds=0, wifi_signal=0, wifi_signal_dbm=0, rpi5_fan_speed=0, drive_temps=0, rpi_power_status=0, ext_sensors=[]):
for param, value in locals().items():
if value:
if param == 'drive_temps' and isinstance(value, dict):
for device, temp in value.items():
entity_id = f"sensor.{hostname.replace("-","_")}_{device}_temp"
state = temp
attributes = config_json(device + "_temp", device, True)
send_sensor_data_to_home_assistant(entity_id, state, attributes)
else:
entity_id = f"sensor.{hostname.replace("-","_")}_{param}"
state = value
attributes = config_json(param, "0", True)
send_sensor_data_to_home_assistant(entity_id, state, attributes)
def send_sensor_data_to_home_assistant(entity_id, state, attributes):
home_assistant_url = config.hass_host
api_token = config.hass_token
url = f"{home_assistant_url}/api/states/{entity_id}"
headers = {
"Authorization": f"Bearer {api_token}",
"Content-Type": "application/json"
}
data = {
"state": state,
"attributes": attributes
}
response = requests.post(url, headers=headers, json=data)
if response.status_code in [200, 201]:
pass
else:
print(f"Failed to update {entity_id}: {response.status_code} - {response.text}")
def publish_to_mqtt(cpu_load=0, cpu_temp=0, used_space=0, voltage=0, sys_clock_speed=0, swap=0, memory=0,
uptime_days=0, uptime_seconds=0, wifi_signal=0, wifi_signal_dbm=0, rpi5_fan_speed=0, drive_temps=0, rpi_power_status=0, ext_sensors=[]):
client = create_mqtt_client()
@@ -645,19 +699,19 @@ def publish_to_mqtt(cpu_load=0, cpu_temp=0, used_space=0, voltage=0, sys_clock_s
if config.cpu_load:
if config.discovery_messages:
client.publish(config.mqtt_discovery_prefix + "/sensor/" + config.mqtt_topic_prefix + "/" + hostname + "_cpuload/config",
config_json('cpuload'), qos=config.qos)
client.publish(config.mqtt_topic_prefix + "/" + hostname + "/cpuload", cpu_load, qos=config.qos, retain=config.retain)
client.publish(config.mqtt_discovery_prefix + "/sensor/" + config.mqtt_topic_prefix + "/" + hostname + "_cpu_load/config",
config_json('cpu_load'), qos=config.qos)
client.publish(config.mqtt_topic_prefix + "/" + hostname + "/cpu_load", cpu_load, qos=config.qos, retain=config.retain)
if config.cpu_temp:
if config.discovery_messages:
client.publish(config.mqtt_discovery_prefix + "/sensor/" + config.mqtt_topic_prefix + "/" + hostname + "_cputemp/config",
config_json('cputemp'), qos=config.qos)
client.publish(config.mqtt_topic_prefix + "/" + hostname + "/cputemp", cpu_temp, qos=config.qos, retain=config.retain)
client.publish(config.mqtt_discovery_prefix + "/sensor/" + config.mqtt_topic_prefix + "/" + hostname + "_cpu_temp/config",
config_json('cpu_temp'), qos=config.qos)
client.publish(config.mqtt_topic_prefix + "/" + hostname + "/cpu_temp", cpu_temp, qos=config.qos, retain=config.retain)
if config.used_space:
if config.discovery_messages:
client.publish(config.mqtt_discovery_prefix + "/sensor/" + config.mqtt_topic_prefix + "/" + hostname + "_diskusage/config",
config_json('diskusage'), qos=config.qos)
client.publish(config.mqtt_topic_prefix + "/" + hostname + "/diskusage", used_space, qos=config.qos, retain=config.retain)
client.publish(config.mqtt_discovery_prefix + "/sensor/" + config.mqtt_topic_prefix + "/" + hostname + "_used_space/config",
config_json('used_space'), qos=config.qos)
client.publish(config.mqtt_topic_prefix + "/" + hostname + "/used_space", used_space, qos=config.qos, retain=config.retain)
if config.voltage:
if config.discovery_messages:
client.publish(config.mqtt_discovery_prefix + "/sensor/" + config.mqtt_topic_prefix + "/" + hostname + "_voltage/config",
@@ -794,12 +848,18 @@ def bulk_publish_to_mqtt(cpu_load=0, cpu_temp=0, used_space=0, voltage=0, sys_cl
def parse_arguments():
parser = argparse.ArgumentParser()
parser.add_argument('--display', '-d', action='store_true', help='display values on screen', default=False)
parser.add_argument('--service', '-s', action='store_true', help='run script as a service, sleep interval is configurable in config.py', default=False)
parser.add_argument('--version', '-v', action='store_true', help='display installed version and exit', default=False)
parser.add_argument('--update', '-u', action='store_true', help='update script and config then exit', default=False)
parser.add_argument('--hass', '-H', action='store_true', help='display Home assistant wake on lan configuration', default=False)
parser = argparse.ArgumentParser(
prog='rpi-mqtt-monitor',
description='Monitor CPU load, temperature, frequency, free space, etc., and publish the data to an MQTT server or Home Assistant API.'
)
parser.add_argument('-H', '--hass_api', action='store_true', help='send readings via Home Assistant API (not via MQTT)', default=False)
parser.add_argument('-d', '--display', action='store_true', help='display values on screen', default=False)
parser.add_argument('-s', '--service', action='store_true', help='run script as a service, sleep interval is configurable in config.py', default=False)
parser.add_argument('-v', '--version', action='store_true', help='display installed version and exit', default=False)
parser.add_argument('-u', '--update', action='store_true', help='update script and config then exit', default=False)
parser.add_argument('-w', '--hass_wake', action='store_true', help='display Home assistant wake on lan configuration', default=False)
args = parser.parse_args()
if args.update:
@@ -826,7 +886,7 @@ def parse_arguments():
print("No update available")
exit()
if args.hass:
if args.hass_wake:
hass_config = """Add this to your Home Assistant switches.yaml file:
- platform: wake_on_lan
@@ -891,11 +951,13 @@ def gather_and_send_info():
if args.display:
print_measured_values(cpu_load, cpu_temp, used_space, voltage, sys_clock_speed, swap, memory, uptime_days, uptime_seconds, wifi_signal, wifi_signal_dbm, rpi5_fan_speed, drive_temps, rpi_power_status, ext_sensors)
if hasattr(config, 'group_messages') and config.group_messages:
bulk_publish_to_mqtt(cpu_load, cpu_temp, used_space, voltage, sys_clock_speed, swap, memory, uptime_days, uptime_seconds, wifi_signal, wifi_signal_dbm, rpi5_fan_speed, drive_temps, rpi_power_status, ext_sensors)
if args.hass_api:
publish_to_hass_api(cpu_load, cpu_temp, used_space, voltage, sys_clock_speed, swap, memory, uptime_days, uptime_seconds, wifi_signal, wifi_signal_dbm, rpi5_fan_speed, drive_temps, rpi_power_status, ext_sensors)
else:
publish_to_mqtt(cpu_load, cpu_temp, used_space, voltage, sys_clock_speed, swap, memory, uptime_days, uptime_seconds, wifi_signal, wifi_signal_dbm, rpi5_fan_speed, drive_temps, rpi_power_status, ext_sensors)
if hasattr(config, 'group_messages') and config.group_messages:
bulk_publish_to_mqtt(cpu_load, cpu_temp, used_space, voltage, sys_clock_speed, swap, memory, uptime_days, uptime_seconds, wifi_signal, wifi_signal_dbm, rpi5_fan_speed, drive_temps, rpi_power_status, ext_sensors)
else:
publish_to_mqtt(cpu_load, cpu_temp, used_space, voltage, sys_clock_speed, swap, memory, uptime_days, uptime_seconds, wifi_signal, wifi_signal_dbm, rpi5_fan_speed, drive_temps, rpi_power_status, ext_sensors)
if not args.service:
break
@@ -959,28 +1021,32 @@ hostname = re.sub(r'[^a-zA-Z0-9_-]', '_', socket.gethostname())
if __name__ == '__main__':
args = parse_arguments();
if args.service:
client = paho.Client()
client.username_pw_set(config.mqtt_user, config.mqtt_password)
client.on_message = on_message
# set will_set to send a message when the client disconnects
client.will_set(config.mqtt_topic_prefix + "/" + hostname + "/status", "0", qos=config.qos, retain=config.retain)
try:
client.connect(config.mqtt_host, int(config.mqtt_port))
except Exception as e:
print("Error connecting to MQTT broker:", e)
sys.exit(1)
if not args.hass_api:
client = paho.Client()
client.username_pw_set(config.mqtt_user, config.mqtt_password)
client.on_message = on_message
# set will_set to send a message when the client disconnects
client.will_set(config.mqtt_topic_prefix + "/" + hostname + "/status", "0", qos=config.qos, retain=config.retain)
try:
client.connect(config.mqtt_host, int(config.mqtt_port))
except Exception as e:
print("Error connecting to MQTT broker:", e)
sys.exit(1)
client.subscribe(config.mqtt_discovery_prefix + "/update/" + hostname + "/command")
print("Listening to topic : " + config.mqtt_discovery_prefix + "/update/" + hostname + "/command")
client.loop_start()
client.subscribe(config.mqtt_discovery_prefix + "/update/" + hostname + "/command")
print("Listening to topic : " + config.mqtt_discovery_prefix + "/update/" + hostname + "/command")
client.loop_start()
thread1 = threading.Thread(target=gather_and_send_info)
thread1.daemon = True # Set thread1 as a daemon thread
thread1.start()
if config.update:
thread2 = threading.Thread(target=update_status)
thread2.daemon = True # Set thread2 as a daemon thread
thread2.start()
if not args.hass_api:
if config.update:
thread2 = threading.Thread(target=update_status)
thread2.daemon = True # Set thread2 as a daemon thread
thread2.start()
try:
while True: