redis beta 1

This commit is contained in:
matt committed 2026-10-13 03:14:25 -07:00
1 parent 63102b7740
commit b23b52ed2e
3 files changed
+34 -27

No files matched your search

+1 -4
View File
@@ -2,7 +2,7 @@
## Summary
This is an API for monitoring Temperature and Humidity data for Xiaomii LYWSD03MMC BLE sensors. The app will scan for any of these devices by looking for the Bluetooth MAC octet and then filtering by model. Once it has a list of MACs, it will then attempt to read the Temp/Humidity data for each MAC and record it. This data can be interacted with through an API.
This is an API for monitoring Temperature and Humidity data for Xiaomii LYWSD03MMC BLE sensors. The app will scan for any of these devices by looking for the Bluetooth MAC octet and then filtering by model. Once it has a list of MACs, it will then attempt to read the Temp/Humidity data for each MAC and record it. This data can be interacted with through an API.
Some product info can be found here at my Knowledgebase:
@@ -12,8 +12,6 @@ https://kb.matt-cloud.com/books/07-saved-articles/page/xiaomii-temperaturehumidi
Aside from a REST API, there are a few more features which need to be added to this app.
* REDIS function for Node.js Websocket support
* In the end this will drive a web dashboard, and I want to be able to view live data on this dashboard
* Friendly Names for Sensors
* There will be a variable added to the `config.py` file to pre-configure friendly names for specific MAC addresses.
* For example, *Living Room* or *Main Bedroom*
@@ -182,7 +180,6 @@ The Python code is split across multiple files with different files having code
ble-scanner/
├─ app.py # Flask + Scheduler + API
├─ ble.py # BLE helper functions to read temp+humidity data
├─ ble-redis.py # Redis functions for Websocket support
├─ scanner.py # Async BLE scan + cache logic
├─ config.py # Shared constants (prefix, interval, etc.)
├─ requirements.txt # Python VENV requirements
+1 -2
View File
@@ -6,8 +6,7 @@ from config import *
####################################################
### Redis Functions
####################################################
if enable_redis:
r = redis.Redis(host=bind_ip, port=redis_port)
r = redis.Redis(host=bind_ip, port=redis_port)
def update_redis(data):
# push data to redis
+32 -21
View File
@@ -20,44 +20,60 @@ _sensors_cache = []
MI_SERVICE_UUID = "ebe0ccb0-7a0a-4b0c-8a1a-6ff2997da3a6"
TEMP_CHAR_UUID = "ebe0ccc1-7a0a-4b0c-8a1a-6ff2997da3a6"
# Redis Functions
r = redis.Redis(host=bind_ip, port=redis_port)
def update_redis(data):
# push data to redis
# Publish to the Redis channel that the WS server is listening on
r.publish('temp_hum', json.dumps(data))
if debug_output:
print("=== Stats Redis Update ===")
print(json.dumps(data, indent=2))
return True
# this function filters MACs
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())
# this function scans for MACs
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)]
# this very important function updates the MAC cache and the sensor objects
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.
# Gets the MACs
new_macs = scan_macs()
print(f"[Scanner] Updated cache, {len(_mac_cache)} unique MAC(s).")
print("Updating data for current MACs...")
# now that we have MACs, let's get sensor data
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}")
# now we update the data in the sensor objects
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 redis is enabled, push full details to the redis server
if enable_redis:
update_redis(get_sensor_details())
# this helper function scans for MACs
def scan_macs():
new_macs = asyncio.run(_scan_once())
with _lock:
@@ -70,58 +86,53 @@ def scan_macs():
_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))
# return just the MACs
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():
# return sensor data and timestamps, can return int or string
def get_sensor_details(friendly_date = False):
sensor_list = []
for sensor in _sensors_cache:
first_seen = sensor.first_seen
last_seen = sensor.last_seen
if friendly_date:
first_seen = ts_to_pst_string(sensor.first_seen)
last_seen = ts_to_pst_string(sensor.last_seen)
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,
"first_seen": first_seen,
"last_seen": last_seen,
"comment": sensor.comment,
"data": sensor.to_dict()
}
sensor_list.append(new_sensor)
result = {
"sensors": sensor_list
}
# print(result)
return result
# return just sensor data without timestamps
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
# helper function for ts int to string
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"))