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 ## What is new
* 2024-12-01: Support for Home Assistant API (no MQTT needed)
* 2024-11-06: External sensors by @pallago * 2024-11-06: External sensors by @pallago
* 2024-10-25: Apt updates sensor * 2024-10-25: Apt updates sensor
* 2024-10-24: Added rpi_power_status 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 ## 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: options:
-h, --help show this help message and exit -h, --help show this help message and exit
--display, -d display values on screen -H, --hass_api send readings via Home Assistant API (not via MQTT)
--service, -s run script as a service -d, --display display values on screen
--version, -v display version -s, --service run script as a service, sleep interval is configurable in config.py
--update, -u update script and config -v, --version display installed version and exit
--hass, -H display Home assistant wake on lan configuration -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: The auto-installer needs the software below and will install it if its not found:
* git
* python (2 or 3) * python (2 or 3)
* python-pip * python-pip
* git * paho-mqtt (python module)
* paho-mqtt * requests (python module)
Only python is not automatically installed, the rest of the dependencies should be handled by the auto installation. 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. 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. 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)) [moved to wiki](../../wiki/Home-Assistant-Integration-(outdated))
## To Do ## To Do
- fix uptime sensor to use timestamp - fix uptime sensor to use timestamp
- integrate hass api - improve hass api integration
## Feature request ## Feature request

View File

@@ -1,3 +1,4 @@
#!/bin/bash
find_python(){ find_python(){
if [[ $(python3 --version) ]]; then if [[ $(python3 --version) ]]; then
python=$(which python3) python=$(which python3)
@@ -78,21 +79,7 @@ install_requirements(){
deactivate deactivate
} }
update_config(){ mqtt_configuration(){
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
printm "MQTT settings" printm "MQTT settings"
printf "Enter mqtt_host: " printf "Enter mqtt_host: "
@@ -126,6 +113,59 @@ update_config(){
if [[ "$CONTROL" =~ ^([yY][eE][sS]|[yY])$ ]]; then if [[ "$CONTROL" =~ ^([yY][eE][sS]|[yY])$ ]]; then
sed -i "s/display_control = False/display_control = True/g" src/config.py sed -i "s/display_control = False/display_control = True/g" src/config.py
fi 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" print_green "+ config.py is updated with provided settings"
@@ -152,8 +192,8 @@ set_cron(){
MIN=2 MIN=2
fi fi
echo "Adding the line below to your crontab" 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${hass_api}"
echo "*/${MIN} * * * * cd ${cwd}; ${python} ${cwd}/src/rpi-cpu2mqtt.py" >> tempcron echo "*/${MIN} * * * * cd ${cwd}; ${python} ${cwd}/src/rpi-cpu2mqtt.py${hass_api}" >> tempcron
crontab tempcron crontab tempcron
fi fi
rm tempcron rm tempcron
@@ -178,7 +218,7 @@ set_service(){
sed -i "s/120/${MIN}/" src/config.py sed -i "s/120/${MIN}/" src/config.py
cwd=$(pwd) cwd=$(pwd)
user=$(whoami) 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/" print_green "+ Copy rpi-mqtt-monitor.service to /etc/systemd/system/"
sudo cp ${cwd}/rpi-mqtt-monitor.service /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 sudo sed -i "s|WorkingDirectory=.*|WorkingDirectory=${cwd}|" /etc/systemd/system/rpi-mqtt-monitor.service
@@ -221,7 +261,7 @@ main(){
done done
printm "Installation is complete." 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." echo "To see all available options run: rpi-mqtt-monitor -h in the terminal."
} }

View File

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

View File

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

View File

@@ -10,6 +10,10 @@ mqtt_port = "1883"
mqtt_discovery_prefix = "homeassistant" mqtt_discovery_prefix = "homeassistant"
mqtt_topic_prefix = "rpi-MQTT-monitor" 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 # Messages configuration
# Uncomment the line bellow to send just one CSV message containing all values (this method don't support HA discovery_messages) # 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 html
import uuid import uuid
import glob import glob
import requests
#import external sensor lib only if one uses external sensors #import external sensor lib only if one uses external sensors
if config.ext_sensors: if config.ext_sensors:
# append folder ext_sensor_lib # append folder ext_sensor_lib
@@ -166,8 +167,9 @@ def check_cpu_temp():
def check_sys_clock_speed(): def check_sys_clock_speed():
full_cmd = "awk '{printf (\"%0.0f\",$1/1000); }' </sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq" full_cmd = "awk '{printf (\"%0.0f\",$1/1000); }' </sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq"
byte_data = subprocess.Popen(full_cmd, shell=True, stdout=subprocess.PIPE).communicate()[0]
return 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): def check_uptime(format):
@@ -392,7 +394,7 @@ def get_release_notes(version):
return release_notes 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() model_name = check_model_name()
manufacturer = get_manufacturer() manufacturer = get_manufacturer()
os = get_os() 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["state_topic"] = config.mqtt_topic_prefix + "/" + hostname + "/" + what_config
data["unique_id"] = hostname + "_" + what_config data["unique_id"] = hostname + "_" + what_config
if what_config == "cpuload": if what_config == "cpu_load":
data["icon"] = "mdi:speedometer" data["icon"] = "mdi:speedometer"
data["name"] = "CPU Usage" data["name"] = "CPU Usage"
data["state_class"] = "measurement" data["state_class"] = "measurement"
data["unit_of_measurement"] = "%" data["unit_of_measurement"] = "%"
elif what_config == "cputemp": elif what_config == "cpu_temp":
data["icon"] = "hass:thermometer" data["icon"] = "hass:thermometer"
data["name"] = "CPU Temperature" data["name"] = "CPU Temperature"
data["unit_of_measurement"] = "°C" data["unit_of_measurement"] = "°C"
data["device_class"] = "temperature" data["device_class"] = "temperature"
data["state_class"] = "measurement" data["state_class"] = "measurement"
elif what_config == "diskusage": elif what_config == "used_space":
data["icon"] = "mdi:harddisk" data["icon"] = "mdi:harddisk"
data["name"] = "Disk Usage" data["name"] = "Disk Usage"
data["unit_of_measurement"] = "%" data["unit_of_measurement"] = "%"
@@ -573,6 +575,20 @@ def config_json(what_config, device="0"):
else: else:
return "" return ""
# Return our built discovery config # 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) return json.dumps(data)
@@ -635,6 +651,44 @@ def publish_update_status_to_mqtt(git_update, apt_updates):
client.disconnect() 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, 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=[]): 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() 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.cpu_load:
if config.discovery_messages: if config.discovery_messages:
client.publish(config.mqtt_discovery_prefix + "/sensor/" + config.mqtt_topic_prefix + "/" + hostname + "_cpuload/config", client.publish(config.mqtt_discovery_prefix + "/sensor/" + config.mqtt_topic_prefix + "/" + hostname + "_cpu_load/config",
config_json('cpuload'), qos=config.qos) config_json('cpu_load'), qos=config.qos)
client.publish(config.mqtt_topic_prefix + "/" + hostname + "/cpuload", cpu_load, qos=config.qos, retain=config.retain) client.publish(config.mqtt_topic_prefix + "/" + hostname + "/cpu_load", cpu_load, qos=config.qos, retain=config.retain)
if config.cpu_temp: if config.cpu_temp:
if config.discovery_messages: if config.discovery_messages:
client.publish(config.mqtt_discovery_prefix + "/sensor/" + config.mqtt_topic_prefix + "/" + hostname + "_cputemp/config", client.publish(config.mqtt_discovery_prefix + "/sensor/" + config.mqtt_topic_prefix + "/" + hostname + "_cpu_temp/config",
config_json('cputemp'), qos=config.qos) config_json('cpu_temp'), qos=config.qos)
client.publish(config.mqtt_topic_prefix + "/" + hostname + "/cputemp", cpu_temp, qos=config.qos, retain=config.retain) client.publish(config.mqtt_topic_prefix + "/" + hostname + "/cpu_temp", cpu_temp, qos=config.qos, retain=config.retain)
if config.used_space: if config.used_space:
if config.discovery_messages: if config.discovery_messages:
client.publish(config.mqtt_discovery_prefix + "/sensor/" + config.mqtt_topic_prefix + "/" + hostname + "_diskusage/config", client.publish(config.mqtt_discovery_prefix + "/sensor/" + config.mqtt_topic_prefix + "/" + hostname + "_used_space/config",
config_json('diskusage'), qos=config.qos) config_json('used_space'), qos=config.qos)
client.publish(config.mqtt_topic_prefix + "/" + hostname + "/diskusage", used_space, qos=config.qos, retain=config.retain) client.publish(config.mqtt_topic_prefix + "/" + hostname + "/used_space", used_space, qos=config.qos, retain=config.retain)
if config.voltage: if config.voltage:
if config.discovery_messages: if config.discovery_messages:
client.publish(config.mqtt_discovery_prefix + "/sensor/" + config.mqtt_topic_prefix + "/" + hostname + "_voltage/config", 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(): def parse_arguments():
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser(
parser.add_argument('--display', '-d', action='store_true', help='display values on screen', default=False) prog='rpi-mqtt-monitor',
parser.add_argument('--service', '-s', action='store_true', help='run script as a service, sleep interval is configurable in config.py', default=False) description='Monitor CPU load, temperature, frequency, free space, etc., and publish the data to an MQTT server or Home Assistant API.'
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('-H', '--hass_api', action='store_true', help='send readings via Home Assistant API (not via MQTT)', default=False)
parser.add_argument('--hass', '-H', action='store_true', help='display Home assistant wake on lan configuration', 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() args = parser.parse_args()
if args.update: if args.update:
@@ -826,7 +886,7 @@ def parse_arguments():
print("No update available") print("No update available")
exit() exit()
if args.hass: if args.hass_wake:
hass_config = """Add this to your Home Assistant switches.yaml file: hass_config = """Add this to your Home Assistant switches.yaml file:
- platform: wake_on_lan - platform: wake_on_lan
@@ -891,7 +951,9 @@ def gather_and_send_info():
if args.display: 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) 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 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:
if hasattr(config, 'group_messages') and config.group_messages: 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) 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: else:
@@ -959,6 +1021,7 @@ hostname = re.sub(r'[^a-zA-Z0-9_-]', '_', socket.gethostname())
if __name__ == '__main__': if __name__ == '__main__':
args = parse_arguments(); args = parse_arguments();
if args.service: if args.service:
if not args.hass_api:
client = paho.Client() client = paho.Client()
client.username_pw_set(config.mqtt_user, config.mqtt_password) client.username_pw_set(config.mqtt_user, config.mqtt_password)
client.on_message = on_message client.on_message = on_message
@@ -973,10 +1036,13 @@ if __name__ == '__main__':
client.subscribe(config.mqtt_discovery_prefix + "/update/" + hostname + "/command") client.subscribe(config.mqtt_discovery_prefix + "/update/" + hostname + "/command")
print("Listening to topic : " + config.mqtt_discovery_prefix + "/update/" + hostname + "/command") print("Listening to topic : " + config.mqtt_discovery_prefix + "/update/" + hostname + "/command")
client.loop_start() client.loop_start()
thread1 = threading.Thread(target=gather_and_send_info) thread1 = threading.Thread(target=gather_and_send_info)
thread1.daemon = True # Set thread1 as a daemon thread thread1.daemon = True # Set thread1 as a daemon thread
thread1.start() thread1.start()
if not args.hass_api:
if config.update: if config.update:
thread2 = threading.Thread(target=update_status) thread2 = threading.Thread(target=update_status)
thread2.daemon = True # Set thread2 as a daemon thread thread2.daemon = True # Set thread2 as a daemon thread