{"id":27944577,"url":"https://github.com/maniam/raspi-enviro-watch","last_synced_at":"2026-05-20T14:35:06.441Z","repository":{"id":290166185,"uuid":"972858204","full_name":"ManiAm/raspi-enviro-watch","owner":"ManiAm","description":"A lightweight system for collecting temperature, humidity, and battery data from multiple BLE environmental sensors (LYWSD03MMC) using a Raspberry Pi. It decodes advertisements, enriches sensor metadata, and forwards the data to a remote InfluxDB database for storage, visualization, and analysis.","archived":false,"fork":false,"pushed_at":"2025-04-27T09:43:16.000Z","size":1141,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-27T10:34:44.764Z","etag":null,"topics":["battery-monitor","ble","bleak","bluepy","gatt","grafana","humidity-monitoring","influxdb","lywsd03mmc","mijia","passive-collection","python","rssi","telinkflasher","temperature-monitoring"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ManiAm.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-04-25T19:32:38.000Z","updated_at":"2025-04-27T09:47:57.000Z","dependencies_parsed_at":"2025-04-27T10:34:53.942Z","dependency_job_id":"7d436356-4013-4026-b088-fcacde6072fb","html_url":"https://github.com/ManiAm/raspi-enviro-watch","commit_stats":null,"previous_names":["maniam/raspi-enviro-watch"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ManiAm%2Fraspi-enviro-watch","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ManiAm%2Fraspi-enviro-watch/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ManiAm%2Fraspi-enviro-watch/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ManiAm%2Fraspi-enviro-watch/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ManiAm","download_url":"https://codeload.github.com/ManiAm/raspi-enviro-watch/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252882813,"owners_count":21819153,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["battery-monitor","ble","bleak","bluepy","gatt","grafana","humidity-monitoring","influxdb","lywsd03mmc","mijia","passive-collection","python","rssi","telinkflasher","temperature-monitoring"],"created_at":"2025-05-07T12:52:58.483Z","updated_at":"2026-05-20T14:35:06.433Z","avatar_url":"https://github.com/ManiAm.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n# Raspi-Enviro-Watch\n\nRaspi-Enviro-Watch is a lightweight environmental monitoring project built around the Raspberry Pi and Xiaomi LYWSD03MMC sensors. It continuously collects real-time temperature, and humidity data from the surrounding environment via Bluetooth Low Energy (BLE).\n\nDesigned for reliability and efficiency, Raspi-Enviro-Watch enables seamless data acquisition suitable for home automation, greenhouse monitoring, or general ambient condition tracking. The collected data is sent to InfluxDB time-series database to enable deeper insights, historical analysis and visualization.\n\n## Wireless Sensors\n\nThere are several wireless temperature and humidity sensors that can report data to a Raspberry Pi 5, using protocols like Bluetooth, Wi-Fi, Zigbee or LoRa.\n\n| Sensor Type                  | Protocol  | Battery Powered | Notes                                                           |\n|------------------------------|-----------|-----------------|-----------------------------------------------------------------|\n| Xiaomi Mijia LYWSD03MMC      | Bluetooth | Yes (CR2032)    | Hackable firmware available; widely used; inexpensive (~$10)    |\n| Inkbird IBS-TH1 Mini         | Bluetooth | Yes             | Good range, supported by python-inkbird                         |\n| RuuviTag                     | Bluetooth | Yes             | High precision; rugged design; supports logging                 |\n| SwitchBot Meter/Meter Plus   | Bluetooth | Yes             | Can work with or without SwitchBot hub                          |\n| Shelly H\u0026T                   | Wi-Fi     | Yes (AAA)       | Easy integration; standalone device with web server             |\n| Sonoff SNZB-02               | Zigbee    | Yes             | Needs Zigbee2MQTT + USB coordinator                             |\n| Dragino LHT65                | LoRaWAN   | Yes (Li-SOCl2)  | Requires LoRaWAN gateway; ultra-long range, low data rates      |\n\nI decided to go with Xiaomi Mijia Thermometer 2!\n\n## Xiaomi Mijia Thermometer 2\n\n​The Xiaomi Mijia Thermometer 2 (model LYWSD03MMC) is a compact and affordable sensor based on Telink TLSR8258 SoC, designed for monitoring environmental conditions. With it's default firmware, it uses Bluetooth Low Energy (BLE) connectivity to ensure low-energy wireless communication, suitable for continuous monitoring without significant battery drain.\n\nIt can measures ambient temperatures ranging from 0°C to 60°C with a resolution of 0.1°C. It can also capture relative humidity levels between 0% and 99% RH with a resolution of 1% RH. It uses a CR2032 coin cell battery and provides battery voltage readings, allowing users to monitor the remaining charge of the battery. Last but not least, it features a LCD display that shows the current temperature and humidity readings, along with comfort level indicators.\n\n\u003cimg src=\"pics/LYWSD03MMC.jpg\" alt=\"segment\" width=\"200\"\u003e\n\nThe availability of hackable firmware provides greater flexibility and control over data formats and broadcast intervals, making them highly adaptable for scalable, battery-efficient environmental monitoring projects. By leveraging BLE and flashing custom firmware, these sensors can passively broadcast temperature and humidity data without requiring active polling, minimizing battery drain and network overhead.\n\nTheir compatibility with popular Python libraries like bleak and bluepy makes integration with the Raspberry Pi 5 straightforward. The LYWSD03MMC is popular among DIY enthusiasts and smart home users. It can be integrated into systems like Home Assistant, Node-RED, or custom InfluxDB setups for real-time monitoring, automation and visualization. Custom [firmware](https://smarthomescene.com/guides/convert-xiaomi-lywsd03mmc-from-bluetooth-to-zigbee/) is also available that can convert the LYWSD03MMC from Bluetooth to Zigbee.\n\n## Getting Started\n\n### Bluetooth Status\n\nCheck that the Raspberry Pi Bluetooth is enabled:\n\nInstall bluez package:\n\n```bash\nsudo apt  install bluez\n```\n\nIf you see `Powered: yes`, Bluetooth is ON.\n\n```bash\nbluetoothctl show | grep Powered\n    Powered: yes\n```\n\n### Scanning for Nearby BLE Devices\n\nSet up a Python virtual environment and install the required dependencies listed in requirements.txt:\n\n```bash\nsudo apt install -y build-essential libglib2.0-dev\ncd ~\npython3 -m venv enviroWatch\nsource enviroWatch/bin/activate\npip install -r requirements.txt\n```\n\nUse the `ble_scan.py` Python script to scan for nearby BLE devices every 2 seconds:\n\n```bash\npython3 ble_scan.py\n```\n\nHere is a sample output:\n\n```text\n+-------------------+----------------------------------------+------------------------------+------------+--------------+-------------+--------+--------+---------+---------+---------------+-----------+\n|        MAC        |                Company                 |         Device Name          | RSSI (dBm) | Distance (m) | AddressType | Paired | Bonded | Trusted | Blocked | LegacyPairing | Connected |\n+-------------------+----------------------------------------+------------------------------+------------+--------------+-------------+--------+--------+---------+---------+---------------+-----------+\n| CB:9A:AA:A4:B1:85 |                Unknown                 |          OBT11C43C           |    -54     |     0.41     |   random    | False  | False  |  False  |  False  |     False     |   False   |\n| BD:8C:DA:61:D5:11 |                Unknown                 |              TY              |    -65     |     2.01     |   public    | False  | False  |  False  |  False  |     False     |   False   |\n| F4:FE:FB:B1:D3:8A |      Samsung Electronics Co.,Ltd       |  [AV] Samsung Soundbar Q90R  |    -68     |     2.8      |   public    | False  | False  |  False  |  False  |     False     |   False   |\n| 76:C3:C5:79:D9:CF |                Unknown                 |           mamaRoo5           |    -76     |     6.45     |   random    | False  | False  |  False  |  False  |     False     |   False   |\n| 4D:08:FF:E7:A6:EC |                Unknown                 |  [LG] webOS TV OLED77CXPUA   |    -83     |    12.61     |   random    | False  | False  |  False  |  False  |     False     |   False   |\n| 88:57:1D:D2:1B:0D |        Seongji Industry Company        |     [oven]Samsung(359F)      |    -88     |    19.73     |   public    | False  | False  |  False  |  False  |     False     |   False   |\n| 5C:C1:D7:D1:9B:53 |      Samsung Electronics Co.,Ltd       | [TV] Samsung Q80 Series (55) |    -89     |    21.52     |   public    | False  | False  |  False  |  False  |     False     |   False   |\n+-------------------+----------------------------------------+------------------------------+------------+--------------+-------------+--------+--------+---------+---------+---------------+-----------+\n```\n\nThe discovered devices are presented in a table, sorted by their signal strength (RSSI) - meaning that closer devices appear at the top of the list. A higher (less negative) RSSI value generally indicates a stronger signal and thus a closer device. For each discovered device, additional metadata is retrieved and displayed in the table. You can find a brief description of each metadata in the table below:\n\n| **Column**        | **Description**                                                                               |\n|-------------------|-----------------------------------------------------------------------------------------------|\n| **MAC**           | The unique hardware address of the Bluetooth device (Media Access Control address).           |\n| **Company**       | The company that manufactured the device, based on MAC address OUI lookup.                    |\n| **Device Name**   | The advertised device name, if available, otherwise marked `unknown`.                         |\n| **RSSI (dBm)**    | Received Signal Strength Indicator showing signal power in decibels relative to 1 milli-watt. |\n| **Distance (m)**  | Estimated distance to the device, calculated based on the RSSI value.                         |\n| **AddressType**   | Indicates whether the device uses a `public` or `random` MAC address.                         |\n| **Paired**        | Whether the device is already paired with the scanner device.                                 |\n| **Bonded**        | Whether the device has been bonded (authenticated and saved) before.                          |\n| **Trusted**       | Whether the device is marked as trusted by the system.                                        |\n| **Blocked**       | Whether the device has been blocked from connecting.                                          |\n| **LegacyPairing** | Indicates if the device uses legacy (older) Bluetooth pairing methods.                        |\n| **Connected**     | Shows if the device is currently connected.                                                   |\n\nWhen the thermometer is turned on, it begins broadcasting BLE advertisements. During the next scan interval, the device should appear in the list of discovered devices, advertising its model number `LYWSD03MMC` as the device name.\n\n```text\n+-------------------+----------------------------------------+------------------------------+------------+--------------+-------------+--------+--------+---------+---------+---------------+-----------+\n|        MAC        |                Company                 |         Device Name          | RSSI (dBm) | Distance (m) | AddressType | Paired | Bonded | Trusted | Blocked | LegacyPairing | Connected |\n+-------------------+----------------------------------------+------------------------------+------------+--------------+-------------+--------+--------+---------+---------+---------------+-----------+\n| A4:C1:38:AA:EA:0D | Telink Semiconductor (Taipei) Co. Ltd. |          LYWSD03MMC          |    -65     |     2.01     |   public    | False  | False  |  False  |  False  |     False     |   False   |\n```\n\nThe default firmware of the device advertises its presence via BLE; however, it does not broadcast actual sensor data (temperature, humidity, battery level) in the advertisement packets. To access the thermometer's readings, you must establish a BLE connection first - then perform a GATT read to retrieve the data.\n\n### GATT (Generic ATTribute Profile)\n\nA GATT profile is a standardized framework that defines how a BLE device organizes its data and operations for communication. Within a GATT profile, the data is grouped into `services`, each representing a specific function or feature of the device (such as monitoring heart rate or battery level). Each service is made up of one or more `characteristics`, which are individual pieces of data or control points that can be interacted with. Every service and characteristic is identified by a globally unique `UUID` (Universally Unique Identifier), which ensures that devices can recognize and interpret them correctly across different manufacturers. In addition to the UUID, each element is also assigned a local `handle`, a numeric reference used internally by the BLE protocol for efficient access. Furthermore, every characteristic has associated `properties` that define how it can be used - for example, whether it can be read, written, or can notify the client when its value changes.\n\n| Property              | Meaning                                                |\n|-----------------------|--------------------------------------------------------|\n| **READ**              | You can read the value.                                |\n| **WRITE**             | You can send (write) a new value.                      |\n| **WRITE NO RESPONSE** | Send data without waiting for acknowledgment (faster). |\n| **NOTIFY**            | Device pushes updates automatically (no need to poll). |\n| **INDICATE**          | Like NOTIFY, but requires acknowledgment by client.    |\n\nYou can use the `discover_service.py` Python script to discover all the GATT services provided by the device, and for each service, it lists all the characteristics along with their UUIDs, handles, and properties. The script essentially maps out the BLE device's service and characteristic structure, helping you understand what data you can access or interact with on that device. Make sure to set the `mac_address` variable in the script.\n\n```bash\npython3 discover_service.py\n```\n\n```text\nService: 00001800-0000-1000-8000-00805f9b34fb\n  Characteristic: 00002a00-0000-1000-8000-00805f9b34fb (Handle: 0x0003) - Properties: READ NOTIFY\n  Characteristic: 00002a01-0000-1000-8000-00805f9b34fb (Handle: 0x0005) - Properties: READ\n  Characteristic: 00002a04-0000-1000-8000-00805f9b34fb (Handle: 0x0007) - Properties: READ\nService: 00001801-0000-1000-8000-00805f9b34fb\n  Characteristic: 00002a05-0000-1000-8000-00805f9b34fb (Handle: 0x000a) - Properties: INDICATE\nService: 0000180a-0000-1000-8000-00805f9b34fb\n  Characteristic: 00002a24-0000-1000-8000-00805f9b34fb (Handle: 0x000e) - Properties: READ\n  Characteristic: 00002a25-0000-1000-8000-00805f9b34fb (Handle: 0x0010) - Properties: READ\n  Characteristic: 00002a26-0000-1000-8000-00805f9b34fb (Handle: 0x0012) - Properties: READ\n  Characteristic: 00002a27-0000-1000-8000-00805f9b34fb (Handle: 0x0014) - Properties: READ\n  Characteristic: 00002a28-0000-1000-8000-00805f9b34fb (Handle: 0x0016) - Properties: READ\n  Characteristic: 00002a29-0000-1000-8000-00805f9b34fb (Handle: 0x0018) - Properties: READ\nService: 00010203-0405-0607-0809-0a0b0c0d1912\n  Characteristic: 00010203-0405-0607-0809-0a0b0c0d2b12 (Handle: 0x001b) - Properties: READ WRITE NO RESPONSE NOTIFY\nService: ebe0ccb0-7a0a-4b0c-8a1a-6ff2997da3a6\n  Characteristic: ebe0ccb7-7a0a-4b0c-8a1a-6ff2997da3a6 (Handle: 0x0020) - Properties: READ WRITE\n  Characteristic: ebe0ccb9-7a0a-4b0c-8a1a-6ff2997da3a6 (Handle: 0x0023) - Properties: READ\n  Characteristic: ebe0ccba-7a0a-4b0c-8a1a-6ff2997da3a6 (Handle: 0x0026) - Properties: READ WRITE\n  Characteristic: ebe0ccbb-7a0a-4b0c-8a1a-6ff2997da3a6 (Handle: 0x0029) - Properties: READ\n  Characteristic: ebe0ccbc-7a0a-4b0c-8a1a-6ff2997da3a6 (Handle: 0x002c) - Properties: NOTIFY\n  Characteristic: ebe0ccbe-7a0a-4b0c-8a1a-6ff2997da3a6 (Handle: 0x0030) - Properties: READ WRITE\n  Characteristic: ebe0ccc1-7a0a-4b0c-8a1a-6ff2997da3a6 (Handle: 0x0033) - Properties: READ NOTIFY\n  Characteristic: ebe0ccc4-7a0a-4b0c-8a1a-6ff2997da3a6 (Handle: 0x0037) - Properties: READ\n  Characteristic: ebe0ccc8-7a0a-4b0c-8a1a-6ff2997da3a6 (Handle: 0x003a) - Properties: WRITE\n  Characteristic: ebe0ccd1-7a0a-4b0c-8a1a-6ff2997da3a6 (Handle: 0x003d) - Properties: WRITE\n  Characteristic: ebe0ccd7-7a0a-4b0c-8a1a-6ff2997da3a6 (Handle: 0x0040) - Properties: READ WRITE\n  Characteristic: ebe0ccd8-7a0a-4b0c-8a1a-6ff2997da3a6 (Handle: 0x0043) - Properties: WRITE\n  Characteristic: ebe0ccd9-7a0a-4b0c-8a1a-6ff2997da3a6 (Handle: 0x0046) - Properties: WRITE NOTIFY\n  Characteristic: ebe0cff1-7a0a-4b0c-8a1a-6ff2997da3a6 (Handle: 0x004a) - Properties: READ WRITE\nService: fafafa00-fafa-fafa-fafa-fafafafafafa\n  Characteristic: fafafa01-fafa-fafa-fafa-fafafafafafa (Handle: 0x004e) - Properties: READ WRITE\nService: 8edffff0-3d1b-9c37-4623-ad7265f14076\n  Characteristic: 8edffff1-3d1b-9c37-4623-ad7265f14076 (Handle: 0x0052) - Properties: READ\n  Characteristic: 8edffff3-3d1b-9c37-4623-ad7265f14076 (Handle: 0x0055) - Properties: NOTIFY\n  Characteristic: 8edffff4-3d1b-9c37-4623-ad7265f14076 (Handle: 0x0059) - Properties: READ WRITE\n  Characteristic: 8edfffef-3d1b-9c37-4623-ad7265f14076 (Handle: 0x005c) - Properties: READ NOTIFY\nService: 0000fe95-0000-1000-8000-00805f9b34fb\n  Characteristic: 00000004-0000-1000-8000-00805f9b34fb (Handle: 0x0061) - Properties: READ\n  Characteristic: 00000010-0000-1000-8000-00805f9b34fb (Handle: 0x0063) - Properties: WRITE NO RESPONSE NOTIFY\n  Characteristic: 00000017-0000-1000-8000-00805f9b34fb (Handle: 0x0066) - Properties: WRITE NOTIFY\n  Characteristic: 00000018-0000-1000-8000-00805f9b34fb (Handle: 0x0069) - Properties: WRITE NO RESPONSE NOTIFY\n  Characteristic: 00000019-0000-1000-8000-00805f9b34fb (Handle: 0x006c) - Properties: WRITE NO RESPONSE NOTIFY\n  Characteristic: 0000001a-0000-1000-8000-00805f9b34fb (Handle: 0x006f) - Properties: WRITE NO RESPONSE NOTIFY\n  Characteristic: 0000001b-0000-1000-8000-00805f9b34fb (Handle: 0x0072) - Properties: WRITE NO RESPONSE NOTIFY\n  Characteristic: 0000001c-0000-1000-8000-00805f9b34fb (Handle: 0x0075) - Properties: WRITE NO RESPONSE NOTIFY\n  ```\n\nServices in BLE can be either `standard` or `vendor-specific`. Standard services are defined by the Bluetooth SIG (Special Interest Group) and are universally recognized across devices to provide common functionality, such as device information and connection management. Vendor-specific services are custom-defined by manufacturers to implement proprietary features or data exchanges that fall outside the official Bluetooth specification; they typically use randomly generated UUIDs and require vendor-specific knowledge to interpret. Additionally, some services are classified as Standard (Company Assigned), meaning the UUID is officially registered by a company (such as Xiaomi) with the Bluetooth SIG, combining elements of standardization with custom functionality.\n\n| Service UUID                           | Service Type                | Description                                                                                              |\n|:---------------------------------------|:----------------------------|:---------------------------------------------------------------------------------------------------------|\n| `00001800-0000-1000-8000-00805f9b34fb` | Standard                    | **Generic Access (GAP)** - device name, appearance, and connection parameters.                           |\n| `00001801-0000-1000-8000-00805f9b34fb` | Standard                    | **Generic Attribute (GATT)** - service change notifications.                                             |\n| `0000180a-0000-1000-8000-00805f9b34fb` | Standard                    | **Device Information Service (DIS)** - model, serial, firmware, and manufacturer info.                   |\n| `00010203-0405-0607-0809-0a0b0c0d1912` | Vendor-Specific             | Custom service - no public standard, device-specific features.                                           |\n| `ebe0ccb0-7a0a-4b0c-8a1a-6ff2997da3a6` | Vendor-Specific             | Common in **Mijia/Xiaomi BLE** sensors - handles environmental data (temp/humidity).                     |\n| `fafafa00-fafa-fafa-fafa-fafafafafafa` | Vendor-Specific             | Placeholder-style custom service - used for custom application-specific data.                            |\n| `8edffff0-3d1b-9c37-4623-ad7265f14076` | Vendor-Specific             | Custom service - possibly for additional sensor data or special commands.                                |\n| `0000fe95-0000-1000-8000-00805f9b34fb` | Standard (Company Assigned) | **Xiaomi Inc. (assigned number)** - special service for Xiaomi BLE devices (e.g., binding, OTA updates). |\n\n### Reading BLE Device Characteristics\n\nThe `sensor_read.py` Python script connects to the BLE device (specified by a MAC address) and reads all readable characteristics from it.\n\n```bash\npython3 sensor_read.py\n```\n\nIt does this in two phases:\n\n- **Phase 1** - Discover Readable Characteristics (using `bluepy`):\n\n    Connects to the BLE device using bluepy.\n\n    Iterates over all services and characteristics.\n\n    Collects the UUIDs of all characteristics that support the \"READ\" property.\n\n    Disconnects from the device after collecting UUIDs.\n\n- **Phase 2** - Read and Decode Data (using `bleak`):\n\n    Asynchronously connects to the same BLE device using bleak.\n\n    For each collected UUID, attempts to read the characteristic value.\n\n    Tries to decode each value into a human-readable UTF-8 string (if possible).\n\nHere is a sample output:\n\n```text\n(00002a00-0000-1000-8000-00805f9b34fb): LYWSD03MMC     ----\u003e Device Name\n(00002a01-0000-1000-8000-00805f9b34fb): Characteristic 00002a01-0000-1000-8000-00805f9b34fb was not found!  ----\u003e Appearance\n(00002a04-0000-1000-8000-00805f9b34fb): Characteristic 00002a04-0000-1000-8000-00805f9b34fb was not found!  ----\u003e Peripheral Preferred Connection\n(00002a24-0000-1000-8000-00805f9b34fb): LYWSD03MMC            ----\u003e Model Number\n(00002a25-0000-1000-8000-00805f9b34fb): F2.0-CFMK-LB-JHBD-03  ----\u003e Serial Number\n(00002a26-0000-1000-8000-00805f9b34fb): 2.1.1_0159            ----\u003e Firmware Revision\n(00002a27-0000-1000-8000-00805f9b34fb): B1.5                  ----\u003e Hardware Revision\n(00002a28-0000-1000-8000-00805f9b34fb): 0159                  ----\u003e Software Revision\n(00002a29-0000-1000-8000-00805f9b34fb): miaomiaoce.com        ----\u003e Manufacturer Name\n\n(00010203-0405-0607-0809-0a0b0c0d2b12):\n(ebe0ccb7-7a0a-4b0c-8a1a-6ff2997da3a6): d15b0000    ----\u003e Time\n(ebe0ccb9-7a0a-4b0c-8a1a-6ff2997da3a6): 0500000006000000\n(ebe0ccba-7a0a-4b0c-8a1a-6ff2997da3a6):\n(ebe0ccbb-7a0a-4b0c-8a1a-6ff2997da3a6): 0500000060540000fc002ef2002b\n(ebe0ccbe-7a0a-4b0c-8a1a-6ff2997da3a6):             ----\u003e Units\n(ebe0ccc1-7a0a-4b0c-8a1a-6ff2997da3a6): bf092c8d0b  ----\u003e temperature, humidity and voltage\n(ebe0ccc4-7a0a-4b0c-8a1a-6ff2997da3a6): d           ----\u003e Battery\n(ebe0ccd7-7a0a-4b0c-8a1a-6ff2997da3a6): 8c0a6c075514\n(ebe0cff1-7a0a-4b0c-8a1a-6ff2997da3a6):\n(fafafa01-fafa-fafa-fafa-fafafafafafa):\n(8edffff1-3d1b-9c37-4623-ad7265f14076): 0500000006000000\n(8edffff4-3d1b-9c37-4623-ad7265f14076):\n(8edfffef-3d1b-9c37-4623-ad7265f14076): bf092c8d0b\n(00000004-0000-1000-8000-00805f9b34fb): 2.1.1_0159\n```\n\nThe second half are Vendor-Specific UUIDs (proprietary Xiaomi services). Through a combination of reverse engineering and research, it is possible to determine how to extract sensor data from the raw hexadecimal values. The `sensor_read_decode.py` Python script can read and decode these sensor parameters.\n\n```bash\npython3 sensor_read_decode.py\n```\n\nSample output:\n\n```text\ntemperature: 21.25 C, humidity: 50 %, voltage: 2.994, estimated_battery: 89 %\nbattery: 100 %\ntime: 1970-01-01 09:08:57\n```\n\nThe standard BLE characteristic for reporting battery level on LYWSD03MMC is unreliable. It consistently shows 99–100% battery, regardless of the actual charge state, and sometimes briefly reports 10% when the battery is nearly depleted. However, by that point, the device typically shuts down before updating the reading.\n\nIn practice, the sensor also transmits the raw battery voltage during normal measurements, which provides a more accurate basis for estimating battery health. [Testing](https://github.com/JsBergbau/MiTemperature2/issues/1#issuecomment-588156894) with an adjustable power supply showed that the sensor operates between 2.1V and 3.0V. When the voltage drops to around 2.1V, the low battery icon appears on the LCD, and the device reports a lower value. This change can take some time - sometimes over an hour - to reflect. Below 2.09V, the sensor turns off completely. Therefore, a more accurate battery percentage estimate is obtained by mapping voltages linearly from 3.1V (100%) to 2.1V (0%).\n\n### Using NOTIFY\n\nIn BLE, a NOTIFY means the device can push updates to you automatically when its value changes - without you needing to poll (read) repeatedly. After subscribing, the device sends the values about every 6 seconds. The `sensor_notify.py` Python script connects to a BLE sensor using the bleak library, subscribes to **EBE0CCC1-7A0A-4B0C-8A1A-6FF2997DA3A6** which is the data characteristic for temperature, humidity, and voltage. It then listens for real-time incoming sensor values for 60 seconds and decodes and prints each value to the console.\n\n```bash\npython3 sensor_notify.py\n```\n\nSample output:\n\n```text\nSubscribed to notifications. Listening...\n2025-04-26 11:15:05 Sender: ebe0ccc1-7a0a-4b0c-8a1a-6ff2997da3a6 Temperature:  23.49 °C Humidity:  45 % Voltage:  3.02 V Estimated Battery:  92 %\n2025-04-26 11:15:11 Sender: ebe0ccc1-7a0a-4b0c-8a1a-6ff2997da3a6 Temperature:  23.51 °C Humidity:  45 % Voltage:  3.02 V Estimated Battery:  92 %\n2025-04-26 11:15:17 Sender: ebe0ccc1-7a0a-4b0c-8a1a-6ff2997da3a6 Temperature:  23.53 °C Humidity:  45 % Voltage:  3.02 V Estimated Battery:  92 %\n2025-04-26 11:15:23 Sender: ebe0ccc1-7a0a-4b0c-8a1a-6ff2997da3a6 Temperature:  23.51 °C Humidity:  45 % Voltage:  3.02 V Estimated Battery:  92 %\n2025-04-26 11:15:29 Sender: ebe0ccc1-7a0a-4b0c-8a1a-6ff2997da3a6 Temperature:  23.53 °C Humidity:  45 % Voltage:  3.02 V Estimated Battery:  92 %\n2025-04-26 11:15:35 Sender: ebe0ccc1-7a0a-4b0c-8a1a-6ff2997da3a6 Temperature:  23.55 °C Humidity:  45 % Voltage:  3.02 V Estimated Battery:  92 %\n2025-04-26 11:15:41 Sender: ebe0ccc1-7a0a-4b0c-8a1a-6ff2997da3a6 Temperature:  23.51 °C Humidity:  45 % Voltage:  3.02 V Estimated Battery:  92 %\n2025-04-26 11:15:47 Sender: ebe0ccc1-7a0a-4b0c-8a1a-6ff2997da3a6 Temperature:  23.54 °C Humidity:  45 % Voltage:  3.02 V Estimated Battery:  92 %\n2025-04-26 11:15:53 Sender: ebe0ccc1-7a0a-4b0c-8a1a-6ff2997da3a6 Temperature:  23.54 °C Humidity:  45 % Voltage:  3.02 V Estimated Battery:  92 %\n2025-04-26 11:15:59 Sender: ebe0ccc1-7a0a-4b0c-8a1a-6ff2997da3a6 Temperature:  23.56 °C Humidity:  45 % Voltage:  3.02 V Estimated Battery:  92 %\n```\n\n### Writing to BLE Device\n\nWe can write data to the  BLE device to control its settings. For example, `sensor_write.py` Python script shows how to change the temperature unit displayed by the device between Celsius (°C) and Fahrenheit (°F). It connects to the BLE device, writes the desired unit setting, and reads back the configured value.\n\n## BLE Key\n\nThe BLE Key (sometimes called the Bind Key) is a security credential used to enable encrypted communication between a BLE device and an application, such as the Mi Home app or custom scripts. It becomes necessary once the device is paired and transitions to using authenticated BLE connections, protecting actions like secure firmware updates. The BLE Key is used in direct Bluetooth connections - for example, when a Raspberry Pi or mobile app communicates with the device over BLE. This key is generated during the initial pairing or binding process within the Xiaomi ecosystem, typically when adding the device through the Mi Home app.\n\nTo obtain the BLE Key for a Xiaomi device, first install the `Xiaomi Home` app on your mobile device. Create an account using your email address and set a password. Once registered, add your BLE device to the app and complete the binding process. After successfully pairing the device, you can retrieve the BLE Key by using the open-source tool [Xiaomi Cloud Tokens Extractor](https://github.com/PiotrMachowski/Xiaomi-cloud-tokens-extractor). Follow the instructions in the repository to run the script.\n\n```bash\npython3 token_extractor.py\n```\n\n```text\n---------\nNAME:     bedroom\nID:       blt.2.1l5u9vpgpo800\nBLE KEY:  4884d1f7e3e907e7f0dce492f61c836e\nMAC:      A4:C1:38:AA:EA:0D\nIP:       76.14.48.209\nTOKEN:    dbcb2c43d519b1f1a2e73b5a\nMODEL:    miaomiaoce.sensor_ht.t2\n---------\nNAME:     outside\nID:       blt.2.1l6ac5vspo800\nBLE KEY:  591f8a6e4e6cf453188ae1eabf16c636\nMAC:      A4:C1:38:67:54:2B\nTOKEN:    6780ca57ec09f49aaeeb50d1\nMODEL:    miaomiaoce.sensor_ht.t2\n```\n\nThe python script securely connects to your Xiaomi account and extracts the `token` and `ble key` associated with your devices. The key is essential for enabling secure BLE communication outside of the Xiaomi ecosystem, such as when connecting via custom scripts or home automation platforms.\n\n## ATC_MiThermometer Custom Firmware\n\nThe default LYWSD03MMC firmware does not broadcast its readings in BLE advertisements. To access data, a direct Bluetooth connection is required. Once connected, the device transmits temperature, humidity, and voltage data approximately every 6 seconds. When you're connected to the device no other connections is accepted, meaning if you hold the connection no other applications can readout sensor data.\n\nThe [ATC_MiThermometer](https://github.com/atc1441/ATC_MiThermometer) project provides custom firmware for LYWSD03MMC, enhancing their functionality for home automation and data logging applications. Flashing ATC_MiThermometer custom firmware enables the sensor to broadcast data in BLE advertisements, allowing for passive data collection without maintaining a continuous connection. This approach is more battery-efficient and facilitates integration with various home automation platforms.\n\nThe custom firmware sends sensor data every minute using the standard Environmental Sensing UUID (`0x181A`), but the data structure inside the advertisement packet follows a custom format defined below:\n\n| Byte Range (0-based) | Description                             | Data Type   |\n|:---------------------|:----------------------------------------|:------------|\n| 0–5                  | MAC address (correct order)             | 6 bytes     |\n| 6–7                  | Temperature (× 10, big-endian, signed)  | int16       |\n| 8                    | Humidity percentage                     | uint8       |\n| 9                    | Battery level percentage                | uint8       |\n| 10–11                | Battery voltage (in mV, big-endian)     | uint16      |\n| 12                   | Frame packet counter                    | uint8       |\n\nThe temperature value is an `int16` and must be divided by 10 to get the actual temperature. The MAC address is sent in correct, human-readable order (not reversed). Furthermore, the frame counter increments with each advertisement, which helps detect missed transmissions. This custom format allows easy parsing without needing encryption keys or active connections.\n\n### Flashing Custom Firmware\n\nThere are two primary methods to flash the ATC_MiThermometer custom firmware onto the LYWSD03MMC device:\n\n- Over-the-Air (OTA):\n\n    - Firmware 2.1.1_0159 requires registration in `Xiaomi Home` app and obtaining IDs. You can obtain device id, token, and BLE key using [Xiaomi Cloud Tokens Extractor](https://github.com/PiotrMachowski/Xiaomi-cloud-tokens-extractor) described in [here](#ble-key).\n    - Next, open the [TelinkFlasher](https://atc1441.github.io/TelinkFlasher.html) in a browser such as Chrome.\n    - Enable experimental web platform features in your browser (ex. chrome://flags/#enable-experimental-web-platform-features)\n    - Click on \"Connect\" to connect to the Xiaomi thermometer; note that discovery may take some time due to the device's low advertising frequency.\n    - Copy and paste device id, token, and BLE key in the form.\n    - Click on \"Do via Login\" to authorize the connection.\n    - Download the [ATC_Exploit.bin](https://github.com/atc1441/ATC_MiThermometer/blob/master/ATC_Exploit.bin) file.\n    - Select the firmware file and initiate the flashing process.​\n\n- USB to UART (Serial):\n\n  For devices that cannot be flashed OTA, the firmware can be uploaded via a USB to UART adapter.\n\n  Refer to the custom firmware repository for detailed instruction.\n\nOnce the custom firmware has been successfully flashed to the BLE device, you can reconnect to it using the same web portal. The device will advertise a new name based on its MAC address, prefixed with \"ATC\" (e.g., `ATC_AAEA0D`). Additionally, the web portal will display a log message such as \"Detected custom Firmware,\" confirming that the custom firmware has been properly loaded onto the device.\n\n### Custom Firmware Configuration\n\nThe ATC_MiThermometer custom firmware allows real-time configuration changes directly from the TelinkFlasher web portal, which are immediately written to the device without requiring a firmware re-flashing. You can configure the following:\n\n- Advertising Type: Select how data is advertised over BLE. Custom format (default) or Mi-Like format (compatible with Xiaomi apps).\n\n- New Measurement Interval: Set how often updated sensor values are broadcasted over BLE (default is 60 seconds).\n\n- Temperature Unit Selection: Choose between °C (Celsius) and °F (Fahrenheit) for display. Default is Celsius.\n\n- Temperature and Humidity Offset Calibration: Apply manual offsets to temperature (±12.8 °C) and humidity (±50%) readings for calibration purposes.\n\n- Instant Advertising on Rapid Changes: Enable immediate BLE advertising if temperature or humidity changes exceed a threshold:\n\n    - Temperature alarm: configurable from 0.1 °C to 25.5 °C (default: 0.5 °C)\n    - Humidity alarm: configurable from 1% to 50% (default: 5%)\n\n- Battery Level Display: Show either battery percentage or humidity percentage alternately every ~5–6 seconds.\n\n- Smiley Face Display Behavior: Control the smiley icon shown on the screen.\n\nOnce you finish changing configurations, click on \"Save current settings in flash\" to make them permanent.\n\n### BLE Passive Data Collection\n\nInstalling the custom firmware on the LYWSD03MMC enables passive data collection by broadcasting sensor readings over BLE advertisements without requiring an active connection. This reduces power consumption and simplifies data retrieval, as devices can simply listen for advertisement packets to collect temperature, humidity, battery level, and voltage information. The data is broadcast using the Environmental Sensing UUID (0x181A) in a custom format, allowing for easy decoding.\n\nFurthermore, since no BLE connection or pairing is necessary, multiple sensors can be monitored simultaneously with minimal system resources, making this method ideal for scalable, low-power sensor networks and continuous home environment monitoring. The `sensor_collector.py` script passively listens for BLE advertisement packets from different sensors, decodes the broadcasted data fields, and displays the parsed measurements.\n\n```bash\npython3 sensor_collector.py\n```\n\n```text\n[+] Received Data:\n      timestamp: 2025-04-27 20:59:44\n      temperature: 72.1\n      humidity: 47\n      battery_level: 90\n      battery_voltage: 3.012\n      frame_counter: 157\n      rssi: -72\n      distance: 4.29\n      measurement_interval: 52.99\n      name: sensor1\n      location: bedroom\n      mac_address: A4:C1:38:AA:EA:0D\n```\n\nNote that the sensor data + frame counter inside the BLE advertisement is updated once per minute. However, BLE advertising happens much more often than every minute. In other words, between each \"real\" update, the sensor repeatedly advertises the same data (same frame counter, same temperature/humidity/battery) to maximize the chance that a nearby passive receiver hears it. It is important to implement a deduplication strategy to avoid saving or transmitting identical data multiple times.\n\n## Collector Setup\n\nWhen you have multiple sensor devices sending BLE advertisements, the overall goal is to:\n\n- Centralize the incoming data\n- Store it for future use\n- Visualize it (graphs, dashboards)\n- Act upon it (alerts, automation)\n\nDepending on the number of devices and physical deployment area, you need to carefully plan your collector architecture: single vs multiple collector setup. You can find more information in [here](./README_Collector.md).\n\n## Home Network Deployment\n\nIn my home network, I am adopting the single collector setup approach. A dedicated Raspberry Pi node (`Ares`) acts as the central BLE data collector, passively listening for advertisements from all nearby LYWSD03MMC sensors. I currently have eight BLE sensors placed throughout the house, including one located outside to monitor outdoor temperature and humidity conditions. Instead of storing the collected data locally, I forward the decoded data to an external InfluxDB database hosted on a different Raspberry Pi node (`Zeus`), within my cluster.\n\n\u003cimg src=\"pics/home_design.png\" alt=\"segment\" width=\"450\"\u003e\n\nThis design separates the collection and storage responsibilities, ensuring that the collector remains lightweight and focused solely on BLE scanning and decoding, while Zeus handles storage, querying, and visualization workloads. The setup provides a simple, efficient, and scalable architecture that fits the needs of my home lab environment while following good distributed system practices.\n\nThe implementation is provided in the `sensor_collect_forward.py` script. It builds upon the functionality of `sensor_collector.py`, but instead of printing the decoded sensor values to stdout, it forwards the data to a remote InfluxDB time-series database. The measurement is named `ble_sensor`, and metadata such as the collector's hostname, username, sensor name, sensor location, and sensor MAC address are included as `tags` to support efficient indexing and querying.\n\nTo ensure clean and efficient data storage, a deduplication strategy is implemented. Each received (MAC address, frame counter) pair is stored as a key in a Redis in-memory database with an automatic expiration of 2 minutes. If the same key is encountered again, the data is identified as a duplicate and skipped. This approach provides fast, scalable, and persistent deduplication even across collector restarts. This ensures that only unique sensor readings are forwarded to the InfluxDB database.\n\nStart a container running Redis:\n\n```bash\ndocker compose up -d\n```\n\nStart the main Python script:\n\n```bash\npython3 sensor_collect_forward.py\n```\n\nHere is a sample visualization in Grafana:\n\n\u003cimg src=\"pics/home_sensors.jpg\" alt=\"segment\" width=\"600\"\u003e\n\nOne particularly insightful observation involves monitoring bathroom humidity levels. The data reveals how effective the exhaust fan is at removing moisture after a shower, providing a measure of ventilation performance and air quality improvement.\n\n\u003cimg src=\"pics/home_humidity.jpg\" alt=\"segment\" width=\"600\"\u003e\n\n\n### Run as a systemd Service\n\nTo run the project in the background and start it on system boot:\n\n1. Copy the service file:\n\n```bash\nsudo cp enviro-watch.service /etc/systemd/system/enviro-watch.service\n```\n\n2. Reload systemd and start the service:\n\n```bash\nsudo systemctl daemon-reexec\nsudo systemctl daemon-reload\nsudo systemctl enable enviro-watch\nsudo systemctl start enviro-watch\n```\n\n3. Check status and logs:\n\n```bash\nsudo systemctl status enviro-watch\n```\n\n4. On service failure check the journal logs:\n\n```bash\njournalctl -u enviro-watch -n 50 --no-pager\n```\n\nEnsure that your virtual environment and script paths are correctly set in the service file.\n\n-----------------------\n\nNode-RED for data visualization and automation.\n\nCharacteristic: ebe0ccbc-7a0a-4b0c-8a1a-6ff2997da3a6 (Handle: 0x002c) - Properties: NOTIFY  --\u003e history\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaniam%2Fraspi-enviro-watch","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmaniam%2Fraspi-enviro-watch","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaniam%2Fraspi-enviro-watch/lists"}