# -*- coding: UTF-8 -*-
# This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it).
# Copyright (c) 2019-2023 Raffaello Bonghi.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
from .common import cat, check_file
import os
import re
# Logging
import logging
# Create logger
logger = logging.getLogger(__name__)
TEMPERATURE_RE = re.compile(r'^temp(?P\d+)_label$')
TEMPERATURE_OFFLINE = -256
def read_temperature(data):
values = {}
for name, path in data.items():
try:
value = float(cat(path)) / 1000.0
values[name] = value
except (OSError, ValueError):
# If negative sensor offline
values[name] = TEMPERATURE_OFFLINE
return values
def get_virtual_thermal_temperature(thermal_path):
temperature = {}
# Find all thermal zone available
items = os.listdir(thermal_path)
subdirectories = ["{path}/{item}".format(path=thermal_path, item=item)
for item in items if os.path.isdir(os.path.join(thermal_path, item)) and 'thermal_' in item]
# Check if is a real temperature
idx = 0
for thermal_path in subdirectories:
path_name = "{path}/type".format(path=thermal_path)
path_value = "{path}/temp".format(path=thermal_path)
if os.path.isfile(path_name) and os.path.isfile(path_value):
# Decode name
raw_name = cat(path_name).strip()
name = raw_name.split("-")[0] if '-' in raw_name else raw_name.split("_")[0]
# Remove PMIC temperature (TX family)
if 'PMIC' in name:
logger.warn("Skipped PMIC")
continue
# Store new temperature
name = name if name not in temperature else "{name}{idx}".format(name=name, idx=idx)
idx = idx if name not in temperature else idx + 1
# Check if is readable and accessible
if check_file(os.path.join(thermal_path, "temp")):
temperature[name] = {'temp': os.path.join(thermal_path, "temp")}
# Message detected
logger.info("Found thermal \"{name}\" in {path}".format(name=name, path=os.path.basename(thermal_path)))
# Sort all temperatures
return temperature
def get_hwmon_thermal_system(root_dir):
sensor_name = {}
if not os.path.isdir(root_dir):
logger.error("Folder {root_dir} doesn't exist".format(root_dir=root_dir))
return sensor_name
# Fin all fans
for dir in os.listdir(root_dir):
path = os.path.join(root_dir, dir)
if not os.path.isdir(path):
continue
# Find all pwm in folder
for file in os.listdir(path):
name_label_path = os.path.join(path, file)
# Check if there is a label
match = re.search(TEMPERATURE_RE, file)
if not match:
continue
parsed_name = match.groupdict()
# Build list current and average power read
number_port = int(parsed_name['num'])
# Read name
raw_name = cat(name_label_path).strip()
logger.info("Found temperature sensor: {name}".format(name=raw_name))
# Build list of path
path_crit_alarm = os.path.join(path, "temp{num}_crit_alarm".format(num=number_port))
if check_file(path_crit_alarm):
warnings = {'crit_alarm': path_crit_alarm}
values = read_temperature(warnings)
logger.info("Alarms {name} - {data}".format(name=raw_name, data=values))
# Read Temperatures
sensor = {}
if check_file(os.path.join(path, "temp{num}_input".format(num=number_port))):
sensor['temp'] = os.path.join(path, "temp{num}_input".format(num=number_port)) # Temperature in deg
if check_file(os.path.join(path, "temp{num}_input".format(num=number_port))):
sensor['max'] = os.path.join(path, "temp{num}_max".format(num=number_port)) # Temperature in deg
if check_file(os.path.join(path, "temp{num}_input".format(num=number_port))):
sensor['crit'] = os.path.join(path, "temp{num}_crit".format(num=number_port)) # Temperature in deg
# If there is an file is added in list
if sensor:
sensor_name[raw_name] = sensor
return sensor_name
class TemperatureService(object):
def __init__(self):
self._temperature = {}
# Find all temperature available
sys_folder = "/sys"
if os.getenv('JTOP_TESTING', False):
logger.warning("Running in JTOP_TESTING folder={root_dir}".format(root_dir=sys_folder))
# Build folders
hwmon_dir = os.path.join(sys_folder, "class", "hwmon")
thermal_path = os.path.join(sys_folder, "devices", "virtual", "thermal")
if os.path.isdir(thermal_path):
# Sort all temperatures
self._temperature = get_virtual_thermal_temperature(thermal_path)
if os.path.isdir(hwmon_dir):
hwmon_temperatures = get_hwmon_thermal_system(hwmon_dir)
self._temperature.update(hwmon_temperatures)
if not self._temperature:
logger.warning("Temperature not folder found!")
# Sort all sensors
self._temperature = dict(sorted(self._temperature.items(), key=lambda item: item[0].lower()))
def get_status(self):
status = {}
# Read temperature from board
for name, sensor in self._temperature.items():
values = read_temperature(sensor)
# Status sensor
values['online'] = values['temp'] != TEMPERATURE_OFFLINE
# Add sensor in dictionary
status[name] = values
return status
# EOF