commit 8b8d90befcd232bffc4a2f8817277b7335b20f1d Author: FedorSarafanov Date: Sat Nov 8 12:15:41 2025 +0300 First commit diff --git a/ATC_233FCD/battery/2025_11/08/ATC_233FCD_battery_2025_11_08.log b/ATC_233FCD/battery/2025_11/08/ATC_233FCD_battery_2025_11_08.log new file mode 100644 index 0000000..e7aebf9 --- /dev/null +++ b/ATC_233FCD/battery/2025_11/08/ATC_233FCD_battery_2025_11_08.log @@ -0,0 +1,8 @@ +2025-11-08 11:57:54 100 +2025-11-08 11:58:34 100 +2025-11-08 11:58:54 100 +2025-11-08 11:59:14 100 +2025-11-08 11:59:34 100 +2025-11-08 11:59:54 100 +2025-11-08 12:00:33 100 +2025-11-08 12:00:53 100 diff --git a/ATC_233FCD/humidity/2025_11/08/ATC_233FCD_humidity_2025_11_08.log b/ATC_233FCD/humidity/2025_11/08/ATC_233FCD_humidity_2025_11_08.log new file mode 100644 index 0000000..f21f8dc --- /dev/null +++ b/ATC_233FCD/humidity/2025_11/08/ATC_233FCD_humidity_2025_11_08.log @@ -0,0 +1,8 @@ +2025-11-08 11:57:54 46.10 +2025-11-08 11:58:34 46.09 +2025-11-08 11:58:54 46.08 +2025-11-08 11:59:14 46.10 +2025-11-08 11:59:34 46.12 +2025-11-08 11:59:54 46.20 +2025-11-08 12:00:33 46.15 +2025-11-08 12:00:53 46.20 diff --git a/ATC_233FCD/temperature/2025_11/08/ATC_233FCD_temperature_2025_11_08.log b/ATC_233FCD/temperature/2025_11/08/ATC_233FCD_temperature_2025_11_08.log new file mode 100644 index 0000000..9e6eb21 --- /dev/null +++ b/ATC_233FCD/temperature/2025_11/08/ATC_233FCD_temperature_2025_11_08.log @@ -0,0 +1,8 @@ +2025-11-08 11:57:54 23.00 +2025-11-08 11:58:34 23.03 +2025-11-08 11:58:54 23.03 +2025-11-08 11:59:14 23.06 +2025-11-08 11:59:34 23.07 +2025-11-08 11:59:54 23.05 +2025-11-08 12:00:33 23.06 +2025-11-08 12:00:53 23.10 diff --git a/ATC_233FCD/voltage/2025_11/08/ATC_233FCD_voltage_2025_11_08.log b/ATC_233FCD/voltage/2025_11/08/ATC_233FCD_voltage_2025_11_08.log new file mode 100644 index 0000000..30ea24b --- /dev/null +++ b/ATC_233FCD/voltage/2025_11/08/ATC_233FCD_voltage_2025_11_08.log @@ -0,0 +1,6 @@ +2025-11-08 11:58:24 3.051 +2025-11-08 11:58:44 3.051 +2025-11-08 12:00:03 3.051 +2025-11-08 12:00:23 3.051 +2025-11-08 12:01:03 3.051 +2025-11-08 12:01:23 3.051 diff --git a/ATC_logger.py b/ATC_logger.py new file mode 100644 index 0000000..6ecd373 --- /dev/null +++ b/ATC_logger.py @@ -0,0 +1,152 @@ +import asyncio +import signal +import argparse +from pathlib import Path +from bleak import BleakScanner +from datetime import datetime + +DEBUG_MODE = False + +# Переопределение print для работы с флагом --debug +_original_print = print + +def debug_print(*args, **kwargs): + """Выводит сообщения только в режиме отладки""" + if DEBUG_MODE: + _original_print(*args, **kwargs) + +# Переопределяем глобальный print +print = debug_print + +def log_data(device_id, data_type, value): + + now = datetime.now() + year_month = now.strftime("%Y_%m") + day = now.strftime("%d") + + log_dir = Path(device_id) / data_type / year_month / day + log_dir.mkdir(parents=True, exist_ok=True) + + filename = log_dir / f"{device_id}_{data_type}_{year_month}_{day}.log" + + timestamp = now.strftime("%Y-%m-%d %H:%M:%S") + with open(filename, "a", encoding="utf-8") as f: + f.write(f"{timestamp} {value}\n") + +async def scan_bthome(): + def callback(device, adv_data): + if device.name and device.name.startswith("ATC_"): + device_id = device.name + + srv_data = adv_data.service_data.get("0000fcd2-0000-1000-8000-00805f9b34fb") + if not srv_data: + return + data = bytes(srv_data) + if len(data) < 3: + return + + # Парсинг формата BTHome v2 + # Структура зависит от длины пакета: + # - Пакет 10 байт: только напряжение (байты 4-5) + # - Пакет 11 байт: батарея (байт 4), температура (байты 6-7), влажность (байты 9-10) + + temp = None + hum = None + batt_percent = None + volt = None + + # Проверяем формат BTHome (начинается с 0x40) + if len(data) > 0 and data[0] == 0x40: + if len(data) == 10: + # Пакет только с напряжением + # Байты 4-5: напряжение (little endian, /1000) + if len(data) >= 6: + volt_raw = int.from_bytes(data[4:6], "little", signed=False) + volt = volt_raw / 1000.0 + elif len(data) >= 11: + # Пакет с температурой, влажностью и батареей + # Байт 4: батарея + if len(data) >= 5: + batt_percent = data[4] + # Байты 6-7: температура (little endian, /100) + if len(data) >= 8: + temp_raw = int.from_bytes(data[6:8], "little", signed=False) + temp = temp_raw / 100.0 + # Байты 9-10: влажность (little endian, /100) + if len(data) >= 11: + hum_raw = int.from_bytes(data[9:11], "little", signed=False) + hum = hum_raw / 100.0 + + # Логирование + if temp is not None: + log_data(device_id, "temperature", f"{temp:.2f}") + if hum is not None: + log_data(device_id, "humidity", f"{hum:.2f}") + if batt_percent is not None: + log_data(device_id, "battery", f"{batt_percent}") + if volt is not None: + log_data(device_id, "voltage", f"{volt:.3f}") + + # Выводим только те значения, которые удалось прочитать + values = [] + if temp is not None: + values.append(f"Температура: {temp:.2f} °C") + if hum is not None: + values.append(f"Влажность: {hum:.2f} %") + if batt_percent is not None: + values.append(f"Батарея: {batt_percent} %") + if volt is not None: + values.append(f"Напряжение: {volt:.3f} В") + + if values: + print(f"{device.name} ({device.address})") + print(", ".join(values)) + print("-" * 50) + + print("Сканирование BLE...") + scanner = BleakScanner(callback) + await scanner.start() + + # Создаем событие для остановки + stop_event = asyncio.Event() + + def signal_handler(): + """Обработчик сигналов для корректной остановки""" + stop_event.set() + + # Регистрируем обработчики сигналов через asyncio + loop = asyncio.get_event_loop() + if hasattr(loop, 'add_signal_handler'): + # Linux/Unix + try: + loop.add_signal_handler(signal.SIGINT, signal_handler) + loop.add_signal_handler(signal.SIGTERM, signal_handler) + except NotImplementedError: + # Если add_signal_handler не поддерживается, используем обычный способ + signal.signal(signal.SIGINT, lambda s, f: signal_handler()) + signal.signal(signal.SIGTERM, lambda s, f: signal_handler()) + else: + # Windows + signal.signal(signal.SIGINT, lambda s, f: signal_handler()) + signal.signal(signal.SIGTERM, lambda s, f: signal_handler()) + + # Ждем сигнала завершения + await stop_event.wait() + + await scanner.stop() + print("Сканирование остановлено.") + +def main(): + """Главная функция с парсингом аргументов командной строки""" + global DEBUG_MODE + + parser = argparse.ArgumentParser(description="Сканер BLE устройств BTHome") + parser.add_argument("--debug", action="store_true", help="Включить отладочный вывод") + args = parser.parse_args() + + DEBUG_MODE = args.debug + + asyncio.run(scan_bthome()) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..d7a205e --- /dev/null +++ b/README.md @@ -0,0 +1,53 @@ +# ATC Logger + +Python скрипт для сканирования и логгирования данных с BLE датчиков температуры и влажности LYWSD03MMC с прошивкой ATC. Внутри LYWSD03MMC используется SoC TLSR825x, чип для Bluetooth Low Energy с потреблением порядка 0.5-1 мкА в глубоком сне и 5 мА в момент передачи данных. + +## Требования + +- Python 3.7+ +- BLE адаптер + +## Установка + +```bash +pip install -r requirements.txt +``` + +## Запуск + +```bash +python ATC_logger.py [--debug] +``` + +## Прошивка датчиков LYWSD03MMC + +### Требования к прошивке + +Для корректной работы требуется прошивка **ATC_v5.5 или новее**. Рекомендуется использовать последнюю версию (https://github.com/pvvx/ATC_MiThermometer). + +### Прошивка по UART в Chromium + +- Нужен Chromium или любой Chromium-based браузер. Firefox не поддерживает WebSerial, увы. Сhromium должен быть установлен не из snap/flatpak, а из репозитория - иначе AppArmor не даст работать WebSerial. +- Прошиватор: https://pvvx.github.io/ATC_MiThermometer/USBCOMFlashTx.html +- Выбрать COM порт с подключенным датчиком. Пины подключения погуглить, меняются в зависимости от версии платы. Связь обычно односторонняя, только RX, протокол не настоящий UART, но поверх UART эмулируется. +- Выберите файл прошивки (.bin) +- Нажать "Erase all flash" +- Нажать "Start flashing" + + +### Настройка прошивки с сохранением настроек в Flash +- Убедиться что в настройках прошивки установлены максимальные интервалы измерения и передачи . Для этого использовать веб-интерфейс https://pvvx.github.io/ATC_MiThermometer/TelinkMiFlasher.html + +## Формат логов + +Логи сохраняются в структуру папок: `DeviceID/Type/YYYY_MM/DD/DeviceID_Type_YYYY_MM_DD.log` + +Пример: +``` +ATC_233FCD/temperature/2025_11/08/ATC_233FCD_temperature_2025_11_08.log +``` + +Каждая строка содержит timestamp и значение: +``` +2025-11-08 12:30:15 23.45 +``` diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..9b06fc4 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +bleak>=0.21.0