aioesphomeapi/aioesphomeapi/util.py

58 lines
1.9 KiB
Python

from __future__ import annotations
import math
def fix_float_single_double_conversion(value: float) -> float:
"""Fix precision for single-precision floats and return what was probably
meant as a float.
In ESPHome we work with single-precision floats internally for performance.
But python uses double-precision floats, and when protobuf reads the message
it's auto-converted to a double (which is possible losslessly).
Unfortunately the float representation of 0.1 converted to a double is not the
double representation of 0.1, but 0.10000000149011612.
This methods tries to round to the closest decimal value that a float of this
magnitude can accurately represent.
"""
if value == 0 or not math.isfinite(value):
return value
abs_val = abs(value)
# assume ~7 decimals of precision for floats to be safe
l10 = math.ceil(math.log10(abs_val))
prec = 7 - l10
return round(value, prec)
def host_is_name_part(address: str) -> bool:
"""Return True if a host is the name part."""
return "." not in address and ":" not in address
def address_is_local(address: str) -> bool:
"""Return True if the address is a local address."""
return address.removesuffix(".").endswith(".local")
def build_log_name(
name: str | None, addresses: list[str], connected_address: str | None
) -> str:
"""Return a log name for a connection."""
preferred_address = connected_address
for address in addresses:
if not name and address_is_local(address) or host_is_name_part(address):
name = address.partition(".")[0]
elif not preferred_address:
preferred_address = address
if not preferred_address:
return name or addresses[0]
if (
name
and name != preferred_address
and not preferred_address.startswith(f"{name}.")
):
return f"{name} @ {preferred_address}"
return preferred_address