This commit is contained in:
ai-dev
2025-10-11 12:14:55 +02:00
parent 0085404253
commit e8681cdead
93 changed files with 3680 additions and 671 deletions

View File

@@ -0,0 +1,23 @@
# python 3.11
# Return the list of mountable external Devices
from diskinfo import DiskInfo
di = DiskInfo()
disks = di.get_disk_list(sorting=True)
regex = r"Name:\s+(\w+)\s.*\n"
for d in disks:
if d.get_partition_table_type() == "":
continue
plist = d.get_partition_list()
for item in plist:
label = item.get_fs_label()
if label.startswith("hassos"):
continue
elif label != "":
print(item.get_fs_label())
elif item.get_fs_type() == "apfs":
print("id:{uuid}".format(uuid=item.get_fs_uuid()))
# print(item.get_fs_label()," ",item.get_fs_type()," ",item.get_part_uuid()," ",item.get_name())

View File

@@ -0,0 +1,650 @@
# python 3.11
import logging
import subprocess
import time
import argparse
import collections
import asyncio
import json
import os
import typing
import hashlib
from pySMART import DeviceList, Device
from pySMART.interface import NvmeAttributes, AtaAttributes
import psutil
from psutil._common import sdiskio, sdiskusage
from ha_mqtt_discoverable import Settings, DeviceInfo
from ha_mqtt_discoverable.sensors import Sensor, SensorInfo, BinarySensor
from ha_mqtt_discoverable.sensors import BinarySensorInfo
import signal
from typing import Any, Callable, List, Literal, NoReturn, Self, Tuple, Union
import re
import humanize
from abc import ABC, abstractmethod
from diskinfo import Disk
# config = configparser.ConfigParser( strict=False )
# config.read("/etc/samba/smb.conf")
# Get the arguments from the command-line except the filename
ap = argparse.ArgumentParser()
# Add the arguments to the parser
ap.add_argument("-b", "--broker", required=True, help="Broker")
ap.add_argument("-p", "--port", required=True, help="Broker Port")
ap.add_argument("-u", "--user", required=True, help="Broker User")
ap.add_argument("-P", "--password", required=True, help="Broker Password")
ap.add_argument("-t", "--topic", required=False, help="Topic", default="sambanas")
ap.add_argument(
"-i",
"--hdidle_log",
required=False,
help="HD_IDLE log to listen for sleeping drivers",
)
ap.add_argument(
"-T", "--discovery_topic", required=False, help="Topic", default="homeassistant"
)
ap.add_argument(
"-d", "--persist_discovery", required=False, help="Topic", default=True, type=bool
)
ap.add_argument(
"-v", "--addon_version", required=False, help="Addon Version", default="latest"
)
ap.add_argument(
"-l",
"--logLevel",
required=False,
default="WARNING",
choices=[
"ALL",
"TRACE",
"DEBUG",
"INFO",
"NOTICE",
"WARNING",
"ERROR",
"CRITICAL",
"FATAL",
"OFF",
],
)
args: dict[str, Any] = vars(ap.parse_args())
match str(args["logLevel"]).upper():
case "DEBUG" | "ALL" | "TRACE":
logging.basicConfig(level=logging.DEBUG)
case "NOTICE":
logging.basicConfig(level=logging.INFO)
case "WARNING" | "INFO":
logging.basicConfig(level=logging.WARNING)
case "ERROR":
logging.basicConfig(level=logging.ERROR)
case "CRITICAL" | "FATAL":
logging.basicConfig(level=logging.CRITICAL)
case "OFF":
logging.basicConfig(level=logging.NOTSET)
case "_":
logging.basicConfig(level=logging.WARNING)
mqtt_settings = Settings.MQTT(
host=args["broker"],
port=int(args["port"]),
username=args["user"],
password=args["password"],
discovery_prefix=args["discovery_topic"],
state_prefix=args["topic"],
)
# Global Sensors regitry
class ConfigEntity(ABC):
def __init__(
self,
sensorInfo: Union[SensorInfo, BinarySensorInfo],
state_function: Callable[[Self], Any],
attributes_function: Callable[[Self], dict[str, Any]] | None = None,
) -> None:
self.sensorInfo: SensorInfo | BinarySensorInfo = sensorInfo
self.state_function = state_function
self.attributes_function = attributes_function
self.sensor: Union[Sensor, BinarySensor] = None # type: ignore
def createSensor(self) -> Self:
if self.sensor is not None:
return self.sensor # type: ignore
settings = Settings(mqtt=mqtt_settings, entity=self.sensorInfo)
if isinstance(self.sensorInfo, BinarySensorInfo):
self.sensor = BinarySensor(settings) # type: ignore
logging.debug(
"BinarySensor '%s' created", self.sensor.generate_config()["name"]
)
elif isinstance(self.sensorInfo, SensorInfo):
self.sensor = Sensor(settings) # type: ignore
logging.debug("Sensor '%s' created", self.sensor.generate_config()["name"])
return self
@abstractmethod
def detstroy(self) -> None:
pass
class ConfigEntityAutonomous(ConfigEntity):
def detstroy(self) -> None:
self.sensor.delete()
class ConfigEntityFromDevice(ConfigEntity):
def __init__(
self,
sensorInfo: Union[SensorInfo, BinarySensorInfo],
device: Device,
state_function: Callable[[Self], Any],
attributes_function: Callable[[Self], dict[str, Any]] = None, # type: ignore
):
super().__init__(sensorInfo, state_function, attributes_function)
self.device = device
def detstroy(self) -> None:
self.sensor.delete()
class ConfigEntityFromHDIdle(ConfigEntity):
def __init__(
self,
sensorInfo: Union[SensorInfo, BinarySensorInfo],
device: Device,
state_function: Callable[[Self], Any],
attributes_function: Callable[[Self], dict[str, Any]] = None, # type: ignore
):
super().__init__(sensorInfo, state_function, attributes_function)
self.device = device
def detstroy(self) -> None:
self.sensor.delete()
class ConfigEntityFromIoStat(ConfigEntity):
def __init__(
self,
sensorInfo: Union[SensorInfo, BinarySensorInfo],
iostat_device: str,
state_function: Callable[[Self], Any],
attributes_function: Callable[[Self], dict[str, Any]] | None = None,
) -> None:
super().__init__(sensorInfo, state_function, attributes_function)
self.iostat_device: str = iostat_device
self._iostat: collections.deque[tuple[int, dict[str, sdiskio] | None]] = (
collections.deque([(0, None), (0, None)], maxlen=2)
)
@property
def iostat(self) -> tuple[int, dict[str, sdiskio] | None]:
return self._iostat[1]
@property
def h_iostat(self) -> tuple[int, dict[str, sdiskio] | None]:
return self._iostat[0]
def addIostat(self, iostat: dict) -> None:
self._iostat.append((time.time_ns(), iostat.copy()))
def detstroy(self) -> None:
self.sensor.delete()
class ConfigEntityFromSamba(ConfigEntity):
def __init__(
self,
sensorInfo: Union[SensorInfo, BinarySensorInfo],
samba: dict,
state_function: Callable[[Self], Any],
attributes_function: Callable[[Self], dict[str, Any]] | None = None,
) -> None:
super().__init__(sensorInfo, state_function, attributes_function)
self.samba = samba
def detstroy(self):
self.sensor.delete()
def sambaMetricCollector() -> dict[str, Any]:
# smbstatus gets report of current samba server connections
try:
p = subprocess.Popen(["smbstatus", "-jf"], stdout=subprocess.PIPE)
output, err = p.communicate()
jsondata = json.loads(output.decode())
logging.debug("SmbClient: %s", jsondata)
except Exception:
logging.warning("Exception on smbstat comunication!")
jsondata = {}
data = {"samba_version": jsondata.get("version", "Unknown")}
if "sessions" in jsondata:
data["users"] = len(jsondata["sessions"])
data["users_json"] = jsondata["sessions"]
else:
data["users"] = 0
data["users_json"] = {}
data["connections"] = len(jsondata["tcons"]) if "tcons" in jsondata else 0
if "open_files" in jsondata:
data["open_files"] = len(jsondata["open_files"])
else:
data["open_files"] = 0
logging.debug(data)
return data
sensorList: List[Tuple[str, ConfigEntity]] = []
devicePowerStatus: dict[str, str] = {}
# Main device SambaNas
samba: dict[str, Any] = sambaMetricCollector()
sambanas_device_info = DeviceInfo(
name="SambaNas",
model="Addon",
manufacturer="@Dianlight",
sw_version=samba["samba_version"],
hw_version=args["addon_version"],
identifiers=[os.getenv("HOSTNAME", default="local_test")],
)
devlist = DeviceList()
psdata: dict[str, sdiskio] = psutil.disk_io_counters(perdisk=True, nowrap=True)
for dev_name in psdata.keys():
if re.search(r"\d+$", dev_name):
continue
dev = Device(dev_name)
disk_device_info = DeviceInfo(
name=f"SambaNas Disk {dev_name}",
model=dev.model,
sw_version=dev.firmware,
# connections=[[dev_name,sambanas_device_info.identifiers[0]]],
identifiers=[dev.serial or "Unknown(%s)" % dev_name],
via_device=sambanas_device_info.identifiers[0], # type: ignore
)
# sambanas_device_info.connections.append([dev_name,disk_device_info.identifiers[0]])
def smartAssesmentAttribute(ce: ConfigEntityFromDevice) -> dict[str, Any]:
attributes: dict[str, Any] = {
"smart_capable": ce.device.smart_capable,
"smart_enabled": ce.device.smart_enabled,
"assessment": ce.device.assessment,
"messages": ce.device.messages,
"rotation_rate": ce.device.rotation_rate,
"_test_running": ce.device._test_running,
"_test_progress": ce.device._test_progress,
}
if ce.device.if_attributes is None:
return attributes
if isinstance(ce.device.if_attributes, AtaAttributes):
atattrs: AtaAttributes = ce.device.if_attributes
for atattr in atattrs.legacyAttributes:
if atattr is None:
continue
attributes[atattr.name] = atattr.raw
health_value: Literal["OK", "FAIL"] = (
"OK"
if (atattr.thresh is not None and atattr.worst > atattr.thresh)
else "FAIL"
)
attributes[f"{atattr.name}_Health"] = health_value
elif isinstance(ce.device.if_attributes, NvmeAttributes):
nwmattrs: NvmeAttributes = ce.device.if_attributes
for nwmattr in nwmattrs.__dict__.keys():
if nwmattrs.__dict__[nwmattr] is None:
continue
attributes[nwmattr] = nwmattrs.__dict__[nwmattr]
return attributes
if dev.smart_capable:
smartAssessment = ConfigEntityFromDevice(
sensorInfo=BinarySensorInfo(
name=f"S.M.A.R.T {dev.name}",
unique_id=hashlib.md5(
f"S.M.A.R.T {dev.name}".encode("utf-8")
).hexdigest(),
device=disk_device_info,
# enabled_by_default= dev.smart_capable
device_class="problem",
),
state_function=lambda ce: ce.device.assessment != "PASS",
attributes_function=smartAssesmentAttribute,
device=dev,
)
sensorList.append((f"smart_{dev.name}", smartAssessment.createSensor()))
if args["hdidle_log"] is not None:
hdIdleAssessment = ConfigEntityFromHDIdle(
sensorInfo=BinarySensorInfo(
name=f"POWER {dev.name}",
unique_id=hashlib.md5(f"POWER {dev.name}".encode("utf-8")).hexdigest(),
device=disk_device_info,
device_class="power",
),
state_function=lambda ce: devicePowerStatus.get(ce.device.name) != "spindown",
attributes_function=lambda ce: {"pipe_file": args["hdidle_log"] + "s"},
device=dev,
)
sensorList.append((f"power_{dev.name}", hdIdleAssessment.createSensor()))
def totalDiskRate(ce: ConfigEntityFromIoStat) -> float | Literal[0]:
if (
ce.h_iostat[0] in [0, ce.iostat[0]] or ce.iostat[1] is None or ce.h_iostat[1] is None
):
return 0
t_read: int = int(ce.iostat[1][ce.iostat_device].read_bytes) - int(
ce.h_iostat[1][ce.iostat_device].read_bytes
)
t_write: int = int(ce.iostat[1][ce.iostat_device].write_bytes) - int(
ce.h_iostat[1][ce.iostat_device].write_bytes
)
t_ns_time: int = ce.iostat[0] - ce.h_iostat[0]
logging.debug("%s %d %d %d", ce.iostat_device, t_read, t_write, t_ns_time)
return round(((t_read + t_write) * 1000000000 / t_ns_time) / 1024, 2) # kB/s
def iostatAttribute(ce: ConfigEntityFromIoStat) -> dict[str, str]:
if ce.iostat[1] is None:
return {}
attributes: dict[str, str] = {
key: getattr(ce.iostat[1][ce.iostat_device], key)
for key in ce.iostat[1][ce.iostat_device]._fields
}
return attributes
diskInfo = ConfigEntityFromIoStat(
sensorInfo=SensorInfo(
name=f"IOSTAT {dev.name}",
unique_id=hashlib.md5(f"IOSTAT {dev.name}".encode("utf-8")).hexdigest(),
device=disk_device_info,
unit_of_measurement="kB/s",
device_class="data_rate",
),
state_function=totalDiskRate,
attributes_function=iostatAttribute,
iostat_device=dev.name,
)
sensorList.append((f"iostat_{dev.name}", diskInfo.createSensor()))
partitionDevices: typing.Dict[str, DeviceInfo] = {}
for partition in Disk(dev_name).get_partition_list():
if (partition.get_fs_label() or partition.get_fs_uuid()) == "":
continue
if not partition.get_name() in partitionDevices:
partitionDevices[partition.get_name()] = DeviceInfo(
name=f"SambaNas Partition {partition.get_fs_label() or partition.get_fs_uuid()}",
model=partition.get_fs_label() or partition.get_fs_uuid(),
manufacturer=partition.get_fs_type(),
identifiers=[partition.get_fs_label() or partition.get_fs_uuid()],
via_device=disk_device_info.identifiers[0], # type: ignore
)
try:
sdiskparts = list(
filter(
lambda part: part.device.endswith(partition.get_name()),
psutil.disk_partitions(),
)
)
if sdiskparts and sdiskparts[0] and sdiskparts[0].mountpoint:
if partitionDevices[partition.get_name()].identifiers is None:
partitionDevices[partition.get_name()].identifiers = []
if isinstance(partitionDevices[partition.get_name()].identifiers, str):
partitionDevices[partition.get_name()].identifiers = [
str(partitionDevices[partition.get_name()].identifiers)
]
partitionDevices[partition.get_name()].identifiers.append( # type: ignore
sdiskparts[0].mountpoint
)
finally:
pass
logging.debug(
"Generated %d Partitiond Device for %s", len(partitionDevices), dev.name
)
for partition_device, partitionDeviceInfo in partitionDevices.items():
partitionInfo = ConfigEntityFromIoStat(
sensorInfo=SensorInfo(
name=f"IOSTAT {partitionDeviceInfo.model}",
unique_id=hashlib.md5(
f"IOSTAT {partitionDeviceInfo.model}".encode("utf-8")
).hexdigest(),
device=partitionDeviceInfo,
unit_of_measurement="kB/s",
device_class="data_rate",
),
state_function=totalDiskRate,
attributes_function=iostatAttribute,
iostat_device=partition_device,
)
sensorList.append((f"iostat_{partition_device}", partitionInfo.createSensor()))
def usageAttribute(ce: ConfigEntityAutonomous) -> dict[str, str]:
if ce.sensorInfo.device is None or ce.sensorInfo.device.identifiers is None:
return {}
usage: sdiskusage = psutil.disk_usage(ce.sensorInfo.device.identifiers[-1])
attributes: dict[str, str] = {
"used": humanize.naturalsize(usage.used),
"total": humanize.naturalsize(usage.total),
"free": humanize.naturalsize(usage.free),
}
return attributes
def partitionUsage(ce: ConfigEntityAutonomous) -> Any:
if ce.sensorInfo.device is None or ce.sensorInfo.device.identifiers is None:
return
logging.debug(
"Collecting Usage from %s [%s]",
ce.sensorInfo.device.identifiers,
ce.sensorInfo.device.identifiers[-1],
)
return psutil.disk_usage(ce.sensorInfo.device.identifiers[-1]).percent
if (
partitionDeviceInfo.identifiers is not None and len(partitionDeviceInfo.identifiers) > 1
):
partitionInfo = ConfigEntityAutonomous(
sensorInfo=SensorInfo(
name=f"Usage {partitionDeviceInfo.model}",
unique_id=hashlib.md5(
f"Usage {partitionDeviceInfo.model}".encode("utf-8")
).hexdigest(),
device=partitionDeviceInfo,
icon="mdi:harddisk",
unit_of_measurement="%",
device_class="power_factor",
),
state_function=partitionUsage,
attributes_function=usageAttribute,
)
sensorList.append(
(f"usage_{partition_device}", partitionInfo.createSensor())
)
sambaUsers = ConfigEntityFromSamba(
sensorInfo=SensorInfo(
name="Online Users",
device=sambanas_device_info,
unique_id=hashlib.md5("Online Users".encode("utf-8")).hexdigest(),
),
state_function=lambda ce: ce.samba["users"],
samba=samba,
)
sensorList.append(("samba_users", sambaUsers.createSensor()))
sambaConnections = ConfigEntityFromSamba(
sensorInfo=SensorInfo(
name="Active Connections",
device=sambanas_device_info,
unique_id=hashlib.md5("Active Connections".encode("utf-8")).hexdigest(),
),
state_function=lambda ce: ce.samba["connections"],
attributes_function=lambda ce: {"open_files": ce.samba["open_files"]},
samba=samba,
)
sensorList.append(("samba_connections", sambaConnections.createSensor()))
tasks: list[asyncio.Task] = []
async def publish_states() -> NoReturn:
while True:
for sensor in sensorList:
if isinstance(sensor[1], ConfigEntityAutonomous):
logging.info("Updating sensor %s", sensor[0])
sensor[1].sensor.set_state(sensor[1].state_function(sensor[1])) # type: ignore
if sensor[1].attributes_function is not None:
sensor[1].sensor.set_attributes(
sensor[1].attributes_function(sensor[1])
)
await asyncio.sleep(5)
async def publish_idle_states() -> NoReturn:
with open(args["hdidle_log"]) as pipe_hdidle:
while True:
line = pipe_hdidle.readline()
if len(line) == 0:
logging.error(f"Pipe Broken {args["hdidle_log"]}!")
break
# Parse line with ilde status
# Aug 8 00:14:55 enterprise hd-idle[9958]: sda spindown
# Aug 8 00:14:55 enterprise hd-idle[9958]: sdb spindown
# Aug 8 00:14:56 enterprise hd-idle[9958]: sdc spindown
# Aug 8 00:17:55 enterprise hd-idle[9958]: sdb spinup
# Aug 8 00:28:55 enterprise hd-idle[9958]: sdb spindown
disk, status = line.split(' ', maxsplit=1)
logging.info("Disk %s change to status %s", disk, status.strip())
devicePowerStatus[disk] = status.strip()
logging.debug(devicePowerStatus)
for sensor in sensorList:
if isinstance(sensor[1], ConfigEntityFromHDIdle) and sensor[1].device.name == disk:
logging.info("Updating sensor %s", sensor[0])
if isinstance(sensor[1].sensor, BinarySensor):
sensor[1].sensor.update_state(sensor[1].state_function(sensor[1]))
elif isinstance(sensor[1].sensor, Sensor):
sensor[1].sensor.set_state(sensor[1].state_function(sensor[1])) # type: ignore
if sensor[1].attributes_function is not None:
sensor[1].sensor.set_attributes(
sensor[1].attributes_function(sensor[1])
)
async def publish_device_states() -> NoReturn:
while True:
for sensor in sensorList:
if isinstance(sensor[1], ConfigEntityFromDevice):
logging.info(
"Updating Device sensor %s (pw:%s)",
sensor[0],
devicePowerStatus.get(sensor[1].device.name),
)
if devicePowerStatus.get(sensor[1].device.name) == "spindown":
continue
sensor[1].device.update()
if isinstance(sensor[1].sensor, BinarySensor):
sensor[1].sensor.update_state(sensor[1].state_function(sensor[1]))
elif isinstance(sensor[1].sensor, Sensor):
sensor[1].sensor.set_state(sensor[1].state_function(sensor[1]))
if sensor[1].attributes_function is not None:
sensor[1].sensor.set_attributes(
sensor[1].attributes_function(sensor[1])
)
await asyncio.sleep(5)
async def publish_iostate_states() -> NoReturn:
while True:
iostate: dict[str, sdiskio] = psutil.disk_io_counters(perdisk=True, nowrap=True)
for sensor in sensorList:
if isinstance(sensor[1], ConfigEntityFromIoStat):
logging.info("Updating Iostat sensor %s", sensor[0])
sensor[1].addIostat(iostate)
sensor[1].sensor.set_state(sensor[1].state_function(sensor[1])) # type: ignore
if sensor[1].attributes_function is not None:
sensor[1].sensor.set_attributes(
sensor[1].attributes_function(sensor[1])
)
await asyncio.sleep(1)
async def publish_samba_states() -> NoReturn:
while True:
samba: dict[str, Any] = sambaMetricCollector()
for sensor in sensorList:
if isinstance(sensor[1], ConfigEntityFromSamba):
logging.info("Updating Samba sensor %s", sensor[0])
sensor[1].samba = samba
sensor[1].sensor.set_state(sensor[1].state_function(sensor[1])) # type: ignore
if sensor[1].attributes_function is not None:
sensor[1].sensor.set_attributes(
sensor[1].attributes_function(sensor[1])
)
await asyncio.sleep(10)
async def run() -> None:
def doneCallback(task):
for sensor in sensorList:
logging.info("Unpublish sensor %s", sensor[0])
sensor[1].sensor.delete()
# Loop Status publish
async with asyncio.TaskGroup() as tg:
if args["hdidle_log"] is not None:
task = tg.create_task(publish_idle_states(), name="Read and Publish HD-IDLE states")
task.add_done_callback(doneCallback)
tasks.append(task)
task = tg.create_task(publish_states(), name="Publish States")
task.add_done_callback(doneCallback)
tasks.append(task)
task = tg.create_task(publish_device_states(), name="Publish Device States")
task.add_done_callback(doneCallback)
tasks.append(task)
task = tg.create_task(publish_iostate_states(), name="Publish IO States")
task.add_done_callback(doneCallback)
tasks.append(task)
task = tg.create_task(publish_samba_states(), name="Publish Samba States")
task.add_done_callback(doneCallback)
tasks.append(task)
loop = asyncio.get_event_loop()
async def ask_exit(signame):
logging.warning("Signal %x. Unpublish %d sensors", signame, len(sensorList))
loop.remove_signal_handler(signame)
for task in tasks:
try:
task.cancel()
finally:
logging.warning(f"Task {task.get_name()} cancelled!")
for signame in ('SIGINT', 'SIGTERM', 'SIGHUP'):
loop.add_signal_handler(getattr(signal, signame),
lambda signame=signame: asyncio.create_task(ask_exit(signame)))
await asyncio.gather(*tasks)
if __name__ == "__main__":
asyncio.run(run())

403
samba/rootfs/usr/local/bin/poetry.lock generated Normal file
View File

@@ -0,0 +1,403 @@
# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand.
[[package]]
name = "annotated-types"
version = "0.7.0"
description = "Reusable constraint types to use with typing.Annotated"
optional = false
python-versions = ">=3.8"
files = [
{file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"},
{file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"},
]
[[package]]
name = "chardet"
version = "5.2.0"
description = "Universal encoding detector for Python 3"
optional = false
python-versions = ">=3.7"
files = [
{file = "chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970"},
{file = "chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7"},
]
[[package]]
name = "diskinfo"
version = "3.1.2"
description = "Disk information Python library for Linux"
optional = false
python-versions = ">=3.8"
files = [
{file = "diskinfo-3.1.2-py3-none-any.whl", hash = "sha256:2b8606462b5783d18188ff1a8b7c5094dbc455e8361418033ffe2a823aa3d4b7"},
{file = "diskinfo-3.1.2.tar.gz", hash = "sha256:3f60a6f7b72dbf079c7f828540d38078e977aa5f578aa80a6e50b5860ffeaaa1"},
]
[package.dependencies]
pySMART = ">=1.3.0"
[[package]]
name = "gitlike-commands"
version = "0.3.0"
description = "Makes driver scripts that enable git-like commands so that foo bar will run foo-bar"
optional = false
python-versions = ">=3.9,<4.0"
files = [
{file = "gitlike_commands-0.3.0-py3-none-any.whl", hash = "sha256:c262f8f532639ec8558369bdc2cd904bd0b65638834ed333c42a51be69578f21"},
{file = "gitlike_commands-0.3.0.tar.gz", hash = "sha256:72f4e65239cb6a4a2c614867c5f914b5d5994edd2863335515b543689b01ff70"},
]
[[package]]
name = "ha-mqtt-discoverable"
version = "0.19.2"
description = "Python library for creating MQTT entities compatible with Home Assistant"
optional = false
python-versions = "<4.0,>=3.10.0"
files = [
{file = "ha_mqtt_discoverable-0.19.2-py3-none-any.whl", hash = "sha256:84725816a53d4e64f9d81cac6493c60dbd73899300c169b978fff9fdcbae2344"},
{file = "ha_mqtt_discoverable-0.19.2.tar.gz", hash = "sha256:2c0facdfdff5573a4bae7ab40e9b66cc077e65445fb9d6f356e1c74ce00aa9d9"},
]
[package.dependencies]
gitlike-commands = ">=0.2.1,<0.4.0"
paho-mqtt = "2.1.0"
pyaml = "25.5.0"
pydantic = "2.11.5"
[[package]]
name = "humanfriendly"
version = "10.0"
description = "Human friendly output for text interfaces using Python"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
files = [
{file = "humanfriendly-10.0-py2.py3-none-any.whl", hash = "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477"},
{file = "humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc"},
]
[package.dependencies]
pyreadline3 = {version = "*", markers = "sys_platform == \"win32\" and python_version >= \"3.8\""}
[[package]]
name = "humanize"
version = "4.12.3"
description = "Python humanize utilities"
optional = false
python-versions = ">=3.9"
files = [
{file = "humanize-4.12.3-py3-none-any.whl", hash = "sha256:2cbf6370af06568fa6d2da77c86edb7886f3160ecd19ee1ffef07979efc597f6"},
{file = "humanize-4.12.3.tar.gz", hash = "sha256:8430be3a615106fdfceb0b2c1b41c4c98c6b0fc5cc59663a5539b111dd325fb0"},
]
[package.extras]
tests = ["freezegun", "pytest", "pytest-cov"]
[[package]]
name = "paho-mqtt"
version = "2.1.0"
description = "MQTT version 5.0/3.1.1 client class"
optional = false
python-versions = ">=3.7"
files = [
{file = "paho_mqtt-2.1.0-py3-none-any.whl", hash = "sha256:6db9ba9b34ed5bc6b6e3812718c7e06e2fd7444540df2455d2c51bd58808feee"},
{file = "paho_mqtt-2.1.0.tar.gz", hash = "sha256:12d6e7511d4137555a3f6ea167ae846af2c7357b10bc6fa4f7c3968fc1723834"},
]
[package.extras]
proxy = ["pysocks"]
[[package]]
name = "psutil"
version = "7.0.0"
description = "Cross-platform lib for process and system monitoring in Python. NOTE: the syntax of this script MUST be kept compatible with Python 2.7."
optional = false
python-versions = ">=3.6"
files = [
{file = "psutil-7.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25"},
{file = "psutil-7.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:39db632f6bb862eeccf56660871433e111b6ea58f2caea825571951d4b6aa3da"},
{file = "psutil-7.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fcee592b4c6f146991ca55919ea3d1f8926497a713ed7faaf8225e174581e91"},
{file = "psutil-7.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b1388a4f6875d7e2aff5c4ca1cc16c545ed41dd8bb596cefea80111db353a34"},
{file = "psutil-7.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5f098451abc2828f7dc6b58d44b532b22f2088f4999a937557b603ce72b1993"},
{file = "psutil-7.0.0-cp36-cp36m-win32.whl", hash = "sha256:84df4eb63e16849689f76b1ffcb36db7b8de703d1bc1fe41773db487621b6c17"},
{file = "psutil-7.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:1e744154a6580bc968a0195fd25e80432d3afec619daf145b9e5ba16cc1d688e"},
{file = "psutil-7.0.0-cp37-abi3-win32.whl", hash = "sha256:ba3fcef7523064a6c9da440fc4d6bd07da93ac726b5733c29027d7dc95b39d99"},
{file = "psutil-7.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553"},
{file = "psutil-7.0.0.tar.gz", hash = "sha256:7be9c3eba38beccb6495ea33afd982a44074b78f28c434a1f51cc07fd315c456"},
]
[package.extras]
dev = ["abi3audit", "black (==24.10.0)", "check-manifest", "coverage", "packaging", "pylint", "pyperf", "pypinfo", "pytest", "pytest-cov", "pytest-xdist", "requests", "rstcheck", "ruff", "setuptools", "sphinx", "sphinx_rtd_theme", "toml-sort", "twine", "virtualenv", "vulture", "wheel"]
test = ["pytest", "pytest-xdist", "setuptools"]
[[package]]
name = "pyaml"
version = "25.5.0"
description = "PyYAML-based module to produce a bit more pretty and readable YAML-serialized data"
optional = false
python-versions = ">=3.8"
files = [
{file = "pyaml-25.5.0-py3-none-any.whl", hash = "sha256:b9e0c4e58a5e8003f8f18e802db49fd0563ada587209b13e429bdcbefa87d035"},
{file = "pyaml-25.5.0.tar.gz", hash = "sha256:5799560c7b1c9daf35a7a4535f53e2c30323f74cbd7cb4f2e715b16dd681a58a"},
]
[package.dependencies]
PyYAML = "*"
[package.extras]
anchors = ["unidecode"]
[[package]]
name = "pydantic"
version = "2.11.5"
description = "Data validation using Python type hints"
optional = false
python-versions = ">=3.9"
files = [
{file = "pydantic-2.11.5-py3-none-any.whl", hash = "sha256:f9c26ba06f9747749ca1e5c94d6a85cb84254577553c8785576fd38fa64dc0f7"},
{file = "pydantic-2.11.5.tar.gz", hash = "sha256:7f853db3d0ce78ce8bbb148c401c2cdd6431b3473c0cdff2755c7690952a7b7a"},
]
[package.dependencies]
annotated-types = ">=0.6.0"
pydantic-core = "2.33.2"
typing-extensions = ">=4.12.2"
typing-inspection = ">=0.4.0"
[package.extras]
email = ["email-validator (>=2.0.0)"]
timezone = ["tzdata"]
[[package]]
name = "pydantic-core"
version = "2.33.2"
description = "Core functionality for Pydantic validation and serialization"
optional = false
python-versions = ">=3.9"
files = [
{file = "pydantic_core-2.33.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2b3d326aaef0c0399d9afffeb6367d5e26ddc24d351dbc9c636840ac355dc5d8"},
{file = "pydantic_core-2.33.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e5b2671f05ba48b94cb90ce55d8bdcaaedb8ba00cc5359f6810fc918713983d"},
{file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0069c9acc3f3981b9ff4cdfaf088e98d83440a4c7ea1bc07460af3d4dc22e72d"},
{file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d53b22f2032c42eaaf025f7c40c2e3b94568ae077a606f006d206a463bc69572"},
{file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0405262705a123b7ce9f0b92f123334d67b70fd1f20a9372b907ce1080c7ba02"},
{file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b25d91e288e2c4e0662b8038a28c6a07eaac3e196cfc4ff69de4ea3db992a1b"},
{file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bdfe4b3789761f3bcb4b1ddf33355a71079858958e3a552f16d5af19768fef2"},
{file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:efec8db3266b76ef9607c2c4c419bdb06bf335ae433b80816089ea7585816f6a"},
{file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:031c57d67ca86902726e0fae2214ce6770bbe2f710dc33063187a68744a5ecac"},
{file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:f8de619080e944347f5f20de29a975c2d815d9ddd8be9b9b7268e2e3ef68605a"},
{file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:73662edf539e72a9440129f231ed3757faab89630d291b784ca99237fb94db2b"},
{file = "pydantic_core-2.33.2-cp310-cp310-win32.whl", hash = "sha256:0a39979dcbb70998b0e505fb1556a1d550a0781463ce84ebf915ba293ccb7e22"},
{file = "pydantic_core-2.33.2-cp310-cp310-win_amd64.whl", hash = "sha256:b0379a2b24882fef529ec3b4987cb5d003b9cda32256024e6fe1586ac45fc640"},
{file = "pydantic_core-2.33.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7"},
{file = "pydantic_core-2.33.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246"},
{file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f"},
{file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc"},
{file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de"},
{file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a"},
{file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef"},
{file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e"},
{file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d"},
{file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30"},
{file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf"},
{file = "pydantic_core-2.33.2-cp311-cp311-win32.whl", hash = "sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51"},
{file = "pydantic_core-2.33.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab"},
{file = "pydantic_core-2.33.2-cp311-cp311-win_arm64.whl", hash = "sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65"},
{file = "pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc"},
{file = "pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7"},
{file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025"},
{file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011"},
{file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f"},
{file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88"},
{file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1"},
{file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b"},
{file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1"},
{file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6"},
{file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea"},
{file = "pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290"},
{file = "pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2"},
{file = "pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab"},
{file = "pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f"},
{file = "pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6"},
{file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef"},
{file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a"},
{file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916"},
{file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a"},
{file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d"},
{file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56"},
{file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5"},
{file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e"},
{file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162"},
{file = "pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849"},
{file = "pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9"},
{file = "pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9"},
{file = "pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac"},
{file = "pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5"},
{file = "pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9"},
{file = "pydantic_core-2.33.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a2b911a5b90e0374d03813674bf0a5fbbb7741570dcd4b4e85a2e48d17def29d"},
{file = "pydantic_core-2.33.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6fa6dfc3e4d1f734a34710f391ae822e0a8eb8559a85c6979e14e65ee6ba2954"},
{file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c54c939ee22dc8e2d545da79fc5381f1c020d6d3141d3bd747eab59164dc89fb"},
{file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:53a57d2ed685940a504248187d5685e49eb5eef0f696853647bf37c418c538f7"},
{file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09fb9dd6571aacd023fe6aaca316bd01cf60ab27240d7eb39ebd66a3a15293b4"},
{file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0e6116757f7959a712db11f3e9c0a99ade00a5bbedae83cb801985aa154f071b"},
{file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d55ab81c57b8ff8548c3e4947f119551253f4e3787a7bbc0b6b3ca47498a9d3"},
{file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c20c462aa4434b33a2661701b861604913f912254e441ab8d78d30485736115a"},
{file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:44857c3227d3fb5e753d5fe4a3420d6376fa594b07b621e220cd93703fe21782"},
{file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:eb9b459ca4df0e5c87deb59d37377461a538852765293f9e6ee834f0435a93b9"},
{file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9fcd347d2cc5c23b06de6d3b7b8275be558a0c90549495c699e379a80bf8379e"},
{file = "pydantic_core-2.33.2-cp39-cp39-win32.whl", hash = "sha256:83aa99b1285bc8f038941ddf598501a86f1536789740991d7d8756e34f1e74d9"},
{file = "pydantic_core-2.33.2-cp39-cp39-win_amd64.whl", hash = "sha256:f481959862f57f29601ccced557cc2e817bce7533ab8e01a797a48b49c9692b3"},
{file = "pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5c4aa4e82353f65e548c476b37e64189783aa5384903bfea4f41580f255fddfa"},
{file = "pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d946c8bf0d5c24bf4fe333af284c59a19358aa3ec18cb3dc4370080da1e8ad29"},
{file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87b31b6846e361ef83fedb187bb5b4372d0da3f7e28d85415efa92d6125d6e6d"},
{file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa9d91b338f2df0508606f7009fde642391425189bba6d8c653afd80fd6bb64e"},
{file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2058a32994f1fde4ca0480ab9d1e75a0e8c87c22b53a3ae66554f9af78f2fe8c"},
{file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:0e03262ab796d986f978f79c943fc5f620381be7287148b8010b4097f79a39ec"},
{file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1a8695a8d00c73e50bff9dfda4d540b7dee29ff9b8053e38380426a85ef10052"},
{file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:fa754d1850735a0b0e03bcffd9d4b4343eb417e47196e4485d9cca326073a42c"},
{file = "pydantic_core-2.33.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a11c8d26a50bfab49002947d3d237abe4d9e4b5bdc8846a63537b6488e197808"},
{file = "pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8"},
{file = "pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593"},
{file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612"},
{file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7"},
{file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e"},
{file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8"},
{file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf"},
{file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb"},
{file = "pydantic_core-2.33.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1"},
{file = "pydantic_core-2.33.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:87acbfcf8e90ca885206e98359d7dca4bcbb35abdc0ff66672a293e1d7a19101"},
{file = "pydantic_core-2.33.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:7f92c15cd1e97d4b12acd1cc9004fa092578acfa57b67ad5e43a197175d01a64"},
{file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3f26877a748dc4251cfcfda9dfb5f13fcb034f5308388066bcfe9031b63ae7d"},
{file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac89aea9af8cd672fa7b510e7b8c33b0bba9a43186680550ccf23020f32d535"},
{file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:970919794d126ba8645f3837ab6046fb4e72bbc057b3709144066204c19a455d"},
{file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3eb3fe62804e8f859c49ed20a8451342de53ed764150cb14ca71357c765dc2a6"},
{file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:3abcd9392a36025e3bd55f9bd38d908bd17962cc49bc6da8e7e96285336e2bca"},
{file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:3a1c81334778f9e3af2f8aeb7a960736e5cab1dfebfb26aabca09afd2906c039"},
{file = "pydantic_core-2.33.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2807668ba86cb38c6817ad9bc66215ab8584d1d304030ce4f0887336f28a5e27"},
{file = "pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc"},
]
[package.dependencies]
typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0"
[[package]]
name = "pyreadline3"
version = "3.5.4"
description = "A python implementation of GNU readline."
optional = false
python-versions = ">=3.8"
files = [
{file = "pyreadline3-3.5.4-py3-none-any.whl", hash = "sha256:eaf8e6cc3c49bcccf145fc6067ba8643d1df34d604a1ec0eccbf7a18e6d3fae6"},
{file = "pyreadline3-3.5.4.tar.gz", hash = "sha256:8d57d53039a1c75adba8e50dd3d992b28143480816187ea5efbd5c78e6c885b7"},
]
[package.extras]
dev = ["build", "flake8", "mypy", "pytest", "twine"]
[[package]]
name = "pysmart"
version = "1.4.1"
description = "Wrapper for smartctl (smartmontools)"
optional = false
python-versions = ">=3.8"
files = [
{file = "pySMART-1.4.1-py3-none-any.whl", hash = "sha256:8bb92d0bb579a073c60dca60cb69eea6516d7cf2673ad6d149d2a49e61432e5d"},
{file = "pysmart-1.4.1.tar.gz", hash = "sha256:65700612477861acd83550c33a9430933017c93d91f66973ed409696b0327723"},
]
[package.dependencies]
chardet = "*"
humanfriendly = "*"
[package.extras]
dev = ["coveralls", "pdoc", "pytest", "pytest-cov"]
[[package]]
name = "pyyaml"
version = "6.0.2"
description = "YAML parser and emitter for Python"
optional = false
python-versions = ">=3.8"
files = [
{file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"},
{file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"},
{file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"},
{file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"},
{file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"},
{file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"},
{file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"},
{file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"},
{file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"},
{file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"},
{file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"},
{file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"},
{file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"},
{file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"},
{file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"},
{file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"},
{file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"},
{file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"},
{file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"},
{file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"},
{file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"},
{file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"},
{file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"},
{file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"},
{file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"},
{file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"},
{file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"},
{file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"},
{file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"},
{file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"},
{file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"},
{file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"},
{file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"},
{file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"},
{file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"},
{file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"},
{file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"},
{file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"},
{file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"},
{file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"},
{file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"},
{file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"},
{file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"},
{file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"},
{file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"},
{file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"},
{file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"},
{file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"},
{file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"},
{file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"},
{file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"},
{file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"},
{file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"},
]
[[package]]
name = "typing-extensions"
version = "4.12.2"
description = "Backported and Experimental Type Hints for Python 3.8+"
optional = false
python-versions = ">=3.8"
files = [
{file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"},
{file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
]
[[package]]
name = "typing-inspection"
version = "0.4.0"
description = "Runtime typing introspection tools"
optional = false
python-versions = ">=3.9"
files = [
{file = "typing_inspection-0.4.0-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f"},
{file = "typing_inspection-0.4.0.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122"},
]
[package.dependencies]
typing-extensions = ">=4.12.0"
[metadata]
lock-version = "2.0"
python-versions = "^3.12"
content-hash = "13b19a408fb9cc0752b3e7d593791056598fe9ab78303fef118e37a7b6a3836f"

View File

@@ -0,0 +1,21 @@
[tool.poetry]
name = "sambanas-util"
version = "1.0.1"
description = "Utility for SambaNas Addon"
authors = ["dianlight <lucio.tarantino@gmail.com>"]
license = "MIT"
readme = "README.md"
package-mode = false
[tool.poetry.dependencies]
python = "^3.12"
pySMART = "^1.3.0"
psutil = "^7.0.0"
humanize = "^4.9.0"
diskinfo = "^3.1.1"
ha-mqtt-discoverable = "^0.19.0"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"