128 lines
3.9 KiB
Python
128 lines
3.9 KiB
Python
import asyncio
|
|
import threading
|
|
import time
|
|
from datetime import datetime, timezone
|
|
from zoneinfo import ZoneInfo # Python 3.9+
|
|
from bleak import BleakScanner, BleakClient
|
|
from typing import Set, Iterable
|
|
from config import *
|
|
from sensors import *
|
|
from ble import *
|
|
from redis import *
|
|
|
|
# In-memory cache + thread-safe lock
|
|
_mac_cache: Set[str] = set()
|
|
_lock = threading.Lock()
|
|
_sensors_cache = []
|
|
|
|
# other vars
|
|
MI_SERVICE_UUID = "ebe0ccb0-7a0a-4b0c-8a1a-6ff2997da3a6"
|
|
TEMP_CHAR_UUID = "ebe0ccc1-7a0a-4b0c-8a1a-6ff2997da3a6"
|
|
|
|
def _is_match(mac: str, name: str) -> bool:
|
|
"""Return True if the MAC starts with the configured prefix and is correct model."""
|
|
if name == "LYWSD03MMC":
|
|
return mac.upper().startswith(MAC_PREFIX.upper())
|
|
|
|
|
|
async def _scan_once() -> Iterable[str]:
|
|
"""Do one scan cycle and return the matching MACs."""
|
|
devices = await BleakScanner.discover(timeout=5.0) # 5s scan
|
|
return [dev.address for dev in devices if _is_match(dev.address, dev.name)]
|
|
|
|
def update_cache() -> None:
|
|
"""
|
|
Called by the scheduler (in a separate thread). It runs a tiny
|
|
asyncio event loop to call the Bleak scan, then updates the cache.
|
|
"""
|
|
# Run the async scan in a fresh loop; Bleak requires this.
|
|
new_macs = scan_macs()
|
|
print(f"[Scanner] Updated cache, {len(_mac_cache)} unique MAC(s).")
|
|
print("Updating data for current MACs...")
|
|
sensor_data = None
|
|
try:
|
|
#asyncio.run(update_data())
|
|
sensor_data = sensor_data_query(new_macs)
|
|
print(sensor_data)
|
|
except Exception as e:
|
|
print(f"Aww fuck, no data :(- {e}")
|
|
|
|
for sensor_datum in sensor_data:
|
|
print(f"MAC - {sensor_datum['name']} | temp - {sensor_datum['temp']} | humidity - {sensor_datum['humidity']}")
|
|
for sensor in _sensors_cache:
|
|
if sensor.name == sensor_datum['name']:
|
|
sensor.add_or_update(key="temp", value=sensor_datum['temp'])
|
|
sensor.add_or_update(key="humidity", value=sensor_datum['humidity'])
|
|
|
|
if enable_redis:
|
|
update_redis(get_sensor_details())
|
|
|
|
def scan_macs():
|
|
new_macs = asyncio.run(_scan_once())
|
|
with _lock:
|
|
print("MACs found:")
|
|
for new_mac in new_macs:
|
|
print(f"{new_mac}")
|
|
new_sensor = Sensor(name=f"{new_mac}")
|
|
if new_mac not in set(_mac_cache):
|
|
_sensors_cache.append(new_sensor)
|
|
_mac_cache.update(new_macs)
|
|
return new_macs
|
|
|
|
def update_data():
|
|
for mac in set(_mac_cache):
|
|
print(f"Test - {mac}")
|
|
result = asyncio.run(read_data(mac))
|
|
|
|
def get_cache_snapshot() -> Set[str]:
|
|
"""Return a thread-safe snapshot of the current cache."""
|
|
with _lock:
|
|
return set(_mac_cache)
|
|
|
|
def get_sensor_details():
|
|
sensor_list = []
|
|
|
|
for sensor in _sensors_cache:
|
|
print(f"Summary - {sensor.name}")
|
|
new_sensor = {
|
|
"name": sensor.name,
|
|
# "first_seen": ts_to_pst_string(sensor.first_seen),
|
|
# "last_seen": ts_to_pst_string(sensor.last_seen),
|
|
"first_seen": sensor.first_seen,
|
|
"last_seen": sensor.last_seen,
|
|
"comment": sensor.comment,
|
|
"data": sensor.to_dict()
|
|
|
|
}
|
|
sensor_list.append(new_sensor)
|
|
|
|
result = {
|
|
"sensors": sensor_list
|
|
}
|
|
# print(result)
|
|
return result
|
|
|
|
def get_sensor_summary():
|
|
sensor_list = []
|
|
|
|
for sensor in _sensors_cache:
|
|
print(f"Summary - {sensor.name}")
|
|
new_sensor = {
|
|
"name": sensor.name,
|
|
"comment": sensor.comment,
|
|
"data": sensor.to_dict(simple_return=True)
|
|
|
|
}
|
|
sensor_list.append(new_sensor)
|
|
|
|
result = {
|
|
"sensors": sensor_list
|
|
}
|
|
# print(result)
|
|
return result
|
|
|
|
def ts_to_pst_string(ts_str):
|
|
utc_dt = datetime.fromtimestamp(ts_str, tz=timezone.utc)
|
|
pacific_dt = utc_dt.astimezone(ZoneInfo("America/Los_Angeles"))
|
|
return pacific_dt.strftime("%Y-%m-%d %H:%M:%S")
|