File: temperature.py

package info (click to toggle)
python-asusrouter 1.21.3-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 1,856 kB
  • sloc: python: 20,497; makefile: 3
file content (159 lines) | stat: -rw-r--r-- 5,678 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
"""Temperature endpoint module."""

from __future__ import annotations

import logging
import threading
from typing import Any

from asusrouter.config import ARConfig, ARConfigKey as ARConfKey
from asusrouter.modules.data import AsusData
from asusrouter.modules.wlan import Wlan
from asusrouter.tools.cleaners import clean_content
from asusrouter.tools.converters import safe_float
from asusrouter.tools.readers import read_js_variables
from asusrouter.tools.writers import ensure_notification_flag

_LOGGER = logging.getLogger(__name__)

EXPECTED_DECIMAL_PLACES = 2
EXPECTED_TEMPERATURE_MIN = 10.0
EXPECTED_TEMPERATURE_MAX = 150.0
MAX_SCALE_COUNT = 5

_temperature_warned_lock = threading.Lock()


# The temperature data can be presented it the following JS variables:
# 1) curr_coreTmp_2_raw, curr_coreTmp_5_raw, curr_coreTmp_52_raw
# 2) curr_coreTmp_0_raw, curr_coreTmp_1_raw, curr_coreTmp_2_raw,
# curr_coreTmp_3_raw
# 3) curr_coreTmp_wl0_raw, curr_coreTmp_wl1_raw, curr_coreTmp_wl2_raw,
# curr_coreTmp_wl3_raw
# for 2ghz, 5ghz, 5ghz2, 6ghz respectively
# CPU temperature is set either in curr_cpuTemp or curr_coreTmp_cpu
def read(content: str, **kwargs: Any) -> dict[str, Any]:
    """Read temperature data."""

    temperature: dict[str, Any] = {}

    # Get the correct configuration to proceed
    config = kwargs.get("config", ARConfig)

    # Prepare the content. This page is a set of JS variables
    variables = read_js_variables(clean_content(content))

    # Read WLAN temperatures
    # If there is curr_coreTmp_5_raw, we have type 1
    if "curr_coreTmp_5_raw" in variables:
        temperature[Wlan.FREQ_2G] = variables.get("curr_coreTmp_2_raw")
        temperature[Wlan.FREQ_5G] = variables.get("curr_coreTmp_5_raw")
        temperature[Wlan.FREQ_5G2] = variables.get("curr_coreTmp_52_raw")
    # If there is curr_coreTmp_0_raw, we have type 2
    elif "curr_coreTmp_0_raw" in variables:
        temperature[Wlan.FREQ_2G] = variables.get("curr_coreTmp_0_raw")
        temperature[Wlan.FREQ_5G] = variables.get("curr_coreTmp_1_raw")
        temperature[Wlan.FREQ_5G2] = variables.get("curr_coreTmp_2_raw")
        temperature[Wlan.FREQ_6G] = variables.get("curr_coreTmp_3_raw")
    # If there is curr_coreTmp_wl0_raw, we have type 3
    elif "curr_coreTmp_wl0_raw" in variables:
        temperature[Wlan.FREQ_2G] = variables.get("curr_coreTmp_wl0_raw")
        temperature[Wlan.FREQ_5G] = variables.get("curr_coreTmp_wl1_raw")
        temperature[Wlan.FREQ_5G2] = variables.get("curr_coreTmp_wl2_raw")
        temperature[Wlan.FREQ_6G] = variables.get("curr_coreTmp_wl3_raw")

    # Clean the temperature values from `°C`
    for key, value in temperature.items():
        if value:
            temperature[key] = value.replace("°C", "")

    # Read CPU temperature
    # If there is curr_coreTmp_cpu
    if "curr_coreTmp_cpu" in variables:
        temperature["cpu"] = variables.get("curr_coreTmp_cpu")
    # If there is curr_cpuTemp
    elif "curr_cpuTemp" in variables:
        temperature["cpu"] = variables.get("curr_cpuTemp")

    # Convert the temperature values to float or remove them
    # if they have "disabled"
    temperature = {
        key: safe_float(value)
        for key, value in temperature.items()
        if value and "disabled" not in value
    }

    # While this functional is performing a kind of post-processing,
    # it should stay a part of the read method to have access to the raw data
    if config.get(ARConfKey.OPTIMISTIC_TEMPERATURE):
        temperature, scaled = _scale_temperature(temperature)
        # Check if notification flag is registered
        ensure_notification_flag(
            config, ARConfKey.NOTIFIED_OPTIMISTIC_TEMPERATURE
        )
        with _temperature_warned_lock:
            if (
                scaled
                and config.get(ARConfKey.NOTIFIED_OPTIMISTIC_TEMPERATURE)
                is False
            ):
                _LOGGER.warning(
                    "Temperature values were rescaled due to the issue with "
                    "the raw data. The original data is: "
                    "`%s` and the expected range is between %s and %s.",
                    variables,
                    EXPECTED_TEMPERATURE_MIN,
                    EXPECTED_TEMPERATURE_MAX,
                )
                config.set(ARConfKey.NOTIFIED_OPTIMISTIC_TEMPERATURE, True)

    return temperature


def process(data: dict[str, Any]) -> dict[AsusData, Any]:
    """Process temperature data."""

    temperature: dict[AsusData, Any] = {
        AsusData.TEMPERATURE: data,
    }

    return temperature


def _scale_temperature(
    temperature: dict[str, float | None],
    range_min: float = EXPECTED_TEMPERATURE_MIN,
    range_max: float = EXPECTED_TEMPERATURE_MAX,
    max_scale_count: int = MAX_SCALE_COUNT,
) -> tuple[dict[str, float], bool]:
    """Scale temperature values to a range.

    This method is a temporary solution for those routers
    with orders of magnitude lower temperature values.
    """

    scaled_temperature = {}
    scaled = False

    for key, temp in temperature.items():
        if temp is None:
            continue

        scaled_temp = temp

        count = 0
        # Scale up if too small
        while scaled_temp < range_min and count < max_scale_count:
            scaled_temp *= 10
            count += 1

        # Scale down if too large
        while scaled_temp > range_max and count < max_scale_count:
            scaled_temp /= 10
            count += 1

        if scaled_temp != temp:
            scaled = True
        scaled_temperature[key] = round(scaled_temp, EXPECTED_DECIMAL_PLACES)

    return scaled_temperature, scaled