Compare commits
7 Commits
c5e6187523
...
v2.0.0
| Author | SHA1 | Date | |
|---|---|---|---|
| 4213b6101a | |||
| e9b8d56f6d | |||
| a55d065c38 | |||
| eee68e4034 | |||
| c3dc5677b9 | |||
| fc0399a454 | |||
| 85af215d73 |
16
debian/changelog
vendored
16
debian/changelog
vendored
@@ -1,9 +1,19 @@
|
|||||||
|
sensorpajen (2.0.0) stable; urgency=medium
|
||||||
|
|
||||||
|
* Production release v2.0.0
|
||||||
|
* Modernized service architecture with systemd
|
||||||
|
* Automatic sensor discovery and approval workflow
|
||||||
|
* Fixed state directory for discovered_sensors.json
|
||||||
|
* Improved documentation with installation guide
|
||||||
|
* Bytecode cleanup in postinst for clean installs
|
||||||
|
|
||||||
|
-- Fredrik <fredrik@wahlberg.se> Sun, 28 Dec 2025 10:56:00 +0100
|
||||||
|
|
||||||
sensorpajen (2.0.0-dev) unstable; urgency=medium
|
sensorpajen (2.0.0-dev) unstable; urgency=medium
|
||||||
|
|
||||||
* Initial Debian package release
|
* Initial Debian package release
|
||||||
* Modernized service architecture with systemd
|
* Modernized service architecture with systemd
|
||||||
* Automatic sensor discovery and approval workflow
|
* Automatic sensor discovery and approval workflow
|
||||||
* Environment-based configuration (no .ini files)
|
|
||||||
* System-wide installation with dedicated user
|
|
||||||
|
|
||||||
-- Fredrik <fredrik@wahlberg.se> Fri, 27 Dec 2025 00:00:00 +0000
|
-- Fredrik <fredrik@wahlberg.se> Sun, 28 Dec 2025 09:00:00 +0100
|
||||||
|
|
||||||
|
|||||||
1
debian/files
vendored
1
debian/files
vendored
@@ -1,2 +1 @@
|
|||||||
sensorpajen_2.0.0-dev_all.deb misc optional
|
sensorpajen_2.0.0-dev_all.deb misc optional
|
||||||
sensorpajen_2.0.0-dev_amd64.buildinfo misc optional
|
|
||||||
|
|||||||
21
debian/postinst
vendored
21
debian/postinst
vendored
@@ -14,8 +14,13 @@ case "$1" in
|
|||||||
chown sensorpajen:sensorpajen /etc/sensorpajen
|
chown sensorpajen:sensorpajen /etc/sensorpajen
|
||||||
chmod 750 /etc/sensorpajen
|
chmod 750 /etc/sensorpajen
|
||||||
|
|
||||||
|
# Create state directory with proper permissions (writable at runtime)
|
||||||
|
mkdir -p /var/lib/sensorpajen
|
||||||
|
chown sensorpajen:sensorpajen /var/lib/sensorpajen
|
||||||
|
chmod 750 /var/lib/sensorpajen
|
||||||
|
|
||||||
# Copy example configs to /etc/sensorpajen if they don't exist
|
# Copy example configs to /etc/sensorpajen if they don't exist
|
||||||
for sample in sensorpajen.env.example sensors.json.example discovered_sensors.json.example; do
|
for sample in sensorpajen.env.example sensors.json.example; do
|
||||||
source_file="/usr/share/doc/sensorpajen/examples/$sample"
|
source_file="/usr/share/doc/sensorpajen/examples/$sample"
|
||||||
target_file="/etc/sensorpajen/${sample%.example}"
|
target_file="/etc/sensorpajen/${sample%.example}"
|
||||||
|
|
||||||
@@ -48,7 +53,7 @@ case "$1" in
|
|||||||
venv/bin/pip install -r /opt/sensorpajen/requirements.txt
|
venv/bin/pip install -r /opt/sensorpajen/requirements.txt
|
||||||
else
|
else
|
||||||
echo "Warning: requirements.txt not found, installing bluepy and paho-mqtt directly"
|
echo "Warning: requirements.txt not found, installing bluepy and paho-mqtt directly"
|
||||||
venv/bin/pip install bluepy paho-mqtt
|
venv/bin/pip install bluepy paho-mqtt pybluez
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
@@ -56,6 +61,18 @@ case "$1" in
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Install sensorpajen package itself
|
||||||
|
echo "Installing sensorpajen application..."
|
||||||
|
cd /opt/sensorpajen
|
||||||
|
# Clean up any stale bytecode before building wheel
|
||||||
|
find . -name "*.pyc" -delete
|
||||||
|
find . -name "__pycache__" -type d -delete
|
||||||
|
venv/bin/pip install --no-deps . || {
|
||||||
|
echo "Error: Failed to install sensorpajen package"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
cd /
|
||||||
|
|
||||||
# Set ownership of application directory BEFORE setting capabilities
|
# Set ownership of application directory BEFORE setting capabilities
|
||||||
chown -R sensorpajen:sensorpajen /opt/sensorpajen
|
chown -R sensorpajen:sensorpajen /opt/sensorpajen
|
||||||
|
|
||||||
|
|||||||
2
debian/sensorpajen.service
vendored
2
debian/sensorpajen.service
vendored
@@ -26,7 +26,7 @@ SyslogIdentifier=sensorpajen
|
|||||||
PrivateTmp=true
|
PrivateTmp=true
|
||||||
ProtectSystem=strict
|
ProtectSystem=strict
|
||||||
ProtectHome=true
|
ProtectHome=true
|
||||||
ReadWritePaths=/etc/sensorpajen
|
ReadWritePaths=/etc/sensorpajen /var/lib/sensorpajen
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
|
|||||||
2
debian/sensorpajen/DEBIAN/control
vendored
2
debian/sensorpajen/DEBIAN/control
vendored
@@ -2,7 +2,7 @@ Package: sensorpajen
|
|||||||
Version: 2.0.0-dev
|
Version: 2.0.0-dev
|
||||||
Architecture: all
|
Architecture: all
|
||||||
Maintainer: Fredrik <fredrik@wahlberg.se>
|
Maintainer: Fredrik <fredrik@wahlberg.se>
|
||||||
Installed-Size: 110
|
Installed-Size: 112
|
||||||
Depends: python3 (>= 3.9), python3-venv, python3-pip, bluetooth, bluez, libcap2-bin
|
Depends: python3 (>= 3.9), python3-venv, python3-pip, bluetooth, bluez, libcap2-bin
|
||||||
Recommends: mosquitto-clients
|
Recommends: mosquitto-clients
|
||||||
Section: misc
|
Section: misc
|
||||||
|
|||||||
10
debian/sensorpajen/DEBIAN/md5sums
vendored
10
debian/sensorpajen/DEBIAN/md5sums
vendored
@@ -2,18 +2,18 @@
|
|||||||
0894789523a53bb372980c0906a7d0b5 opt/sensorpajen/requirements.txt
|
0894789523a53bb372980c0906a7d0b5 opt/sensorpajen/requirements.txt
|
||||||
940d73f24eb9f971ce27f9355e3072f3 opt/sensorpajen/scripts/approve-sensors.sh
|
940d73f24eb9f971ce27f9355e3072f3 opt/sensorpajen/scripts/approve-sensors.sh
|
||||||
20eb4f3839b990a530410768897402c0 opt/sensorpajen/src/sensorpajen/__init__.py
|
20eb4f3839b990a530410768897402c0 opt/sensorpajen/src/sensorpajen/__init__.py
|
||||||
1f452c46e42f8dc3751dba6ca68256e9 opt/sensorpajen/src/sensorpajen/approve_sensors.py
|
3c6c65213de874065f81b7b3d8948c8b opt/sensorpajen/src/sensorpajen/approve_sensors.py
|
||||||
da40d9df301d523d517d7cf2809d6f11 opt/sensorpajen/src/sensorpajen/config.py
|
f69225e19918cca05351fa2da8fd7618 opt/sensorpajen/src/sensorpajen/config.py
|
||||||
65c63383dde4f0b249b708f854ec75a3 opt/sensorpajen/src/sensorpajen/discovery_manager.py
|
65c63383dde4f0b249b708f854ec75a3 opt/sensorpajen/src/sensorpajen/discovery_manager.py
|
||||||
592f8a534833c9e403967fcc0ead8eb1 opt/sensorpajen/src/sensorpajen/main.py
|
7604c2bc0a854d6d43ff0f0646386fc5 opt/sensorpajen/src/sensorpajen/main.py
|
||||||
331bf9b314492acc6ce03896367f3cf6 opt/sensorpajen/src/sensorpajen/mqtt_publisher.py
|
331bf9b314492acc6ce03896367f3cf6 opt/sensorpajen/src/sensorpajen/mqtt_publisher.py
|
||||||
5f4ea191e35ce092f39ec0a4f663cb38 opt/sensorpajen/src/sensorpajen/sensor_reader.py
|
5f4ea191e35ce092f39ec0a4f663cb38 opt/sensorpajen/src/sensorpajen/sensor_reader.py
|
||||||
c8dd8fe8fc174a9cd35251fdf80e7b5f opt/sensorpajen/src/sensorpajen/utils.py
|
c8dd8fe8fc174a9cd35251fdf80e7b5f opt/sensorpajen/src/sensorpajen/utils.py
|
||||||
c9c22f9c1d65bfafd89fa45f16b7192b usr/lib/systemd/system/sensorpajen.service
|
b9ad3ea8307d8ed8e938da37ad00f229 usr/lib/systemd/system/sensorpajen.service
|
||||||
4ddb9618c940286f91df901ec818959a usr/share/doc/sensorpajen/INSTALL.md.gz
|
4ddb9618c940286f91df901ec818959a usr/share/doc/sensorpajen/INSTALL.md.gz
|
||||||
bd2f1371c60af415bc9d0dbc1111184d usr/share/doc/sensorpajen/ROADMAP.md.gz
|
bd2f1371c60af415bc9d0dbc1111184d usr/share/doc/sensorpajen/ROADMAP.md.gz
|
||||||
380e8e6b01b757ceac05bc5805844ae4 usr/share/doc/sensorpajen/changelog.Debian.gz
|
380e8e6b01b757ceac05bc5805844ae4 usr/share/doc/sensorpajen/changelog.Debian.gz
|
||||||
14152a98d7cd7fe8daf280aacc4cbf3f usr/share/doc/sensorpajen/examples/discovered_sensors.json.example
|
14152a98d7cd7fe8daf280aacc4cbf3f usr/share/doc/sensorpajen/examples/discovered_sensors.json.example
|
||||||
387cb9ee7f22570312604e2cc07ca7a0 usr/share/doc/sensorpajen/examples/sensorpajen.env.example
|
74c99b732363f93f0a1c134e1a8c3d35 usr/share/doc/sensorpajen/examples/sensorpajen.env.example
|
||||||
292efbddd951c39cb2c9546d5fac5e05 usr/share/doc/sensorpajen/examples/sensors.json.example
|
292efbddd951c39cb2c9546d5fac5e05 usr/share/doc/sensorpajen/examples/sensors.json.example
|
||||||
5f647c63bfc3b174611694779fd215e0 usr/share/doc/sensorpajen/readme.md.gz
|
5f647c63bfc3b174611694779fd215e0 usr/share/doc/sensorpajen/readme.md.gz
|
||||||
|
|||||||
21
debian/sensorpajen/DEBIAN/postinst
vendored
21
debian/sensorpajen/DEBIAN/postinst
vendored
@@ -14,8 +14,13 @@ case "$1" in
|
|||||||
chown sensorpajen:sensorpajen /etc/sensorpajen
|
chown sensorpajen:sensorpajen /etc/sensorpajen
|
||||||
chmod 750 /etc/sensorpajen
|
chmod 750 /etc/sensorpajen
|
||||||
|
|
||||||
|
# Create state directory with proper permissions (writable at runtime)
|
||||||
|
mkdir -p /var/lib/sensorpajen
|
||||||
|
chown sensorpajen:sensorpajen /var/lib/sensorpajen
|
||||||
|
chmod 750 /var/lib/sensorpajen
|
||||||
|
|
||||||
# Copy example configs to /etc/sensorpajen if they don't exist
|
# Copy example configs to /etc/sensorpajen if they don't exist
|
||||||
for sample in sensorpajen.env.example sensors.json.example discovered_sensors.json.example; do
|
for sample in sensorpajen.env.example sensors.json.example; do
|
||||||
source_file="/usr/share/doc/sensorpajen/examples/$sample"
|
source_file="/usr/share/doc/sensorpajen/examples/$sample"
|
||||||
target_file="/etc/sensorpajen/${sample%.example}"
|
target_file="/etc/sensorpajen/${sample%.example}"
|
||||||
|
|
||||||
@@ -48,7 +53,7 @@ case "$1" in
|
|||||||
venv/bin/pip install -r /opt/sensorpajen/requirements.txt
|
venv/bin/pip install -r /opt/sensorpajen/requirements.txt
|
||||||
else
|
else
|
||||||
echo "Warning: requirements.txt not found, installing bluepy and paho-mqtt directly"
|
echo "Warning: requirements.txt not found, installing bluepy and paho-mqtt directly"
|
||||||
venv/bin/pip install bluepy paho-mqtt
|
venv/bin/pip install bluepy paho-mqtt pybluez
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
@@ -56,6 +61,18 @@ case "$1" in
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Install sensorpajen package itself
|
||||||
|
echo "Installing sensorpajen application..."
|
||||||
|
cd /opt/sensorpajen
|
||||||
|
# Clean up any stale bytecode before building wheel
|
||||||
|
find . -name "*.pyc" -delete
|
||||||
|
find . -name "__pycache__" -type d -delete
|
||||||
|
venv/bin/pip install --no-deps . || {
|
||||||
|
echo "Error: Failed to install sensorpajen package"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
cd /
|
||||||
|
|
||||||
# Set ownership of application directory BEFORE setting capabilities
|
# Set ownership of application directory BEFORE setting capabilities
|
||||||
chown -R sensorpajen:sensorpajen /opt/sensorpajen
|
chown -R sensorpajen:sensorpajen /opt/sensorpajen
|
||||||
|
|
||||||
|
|||||||
@@ -153,8 +153,10 @@ def approve_sensor(sensor: DiscoveredSensor, manager: DiscoveryManager):
|
|||||||
print(f" Name: {name}")
|
print(f" Name: {name}")
|
||||||
print(f" Configuration will be reloaded automatically within 15 minutes")
|
print(f" Configuration will be reloaded automatically within 15 minutes")
|
||||||
|
|
||||||
# Mark as approved in discovery manager
|
# Mark as approved in discovery manager and save
|
||||||
|
print(f"\nUpdating discovery status...")
|
||||||
manager.approve(sensor.mac)
|
manager.approve(sensor.mac)
|
||||||
|
print(f"✅ Marked as approved in discovered_sensors.json")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"\n❌ Error saving to sensors.json: {e}")
|
print(f"\n❌ Error saving to sensors.json: {e}")
|
||||||
@@ -172,7 +174,7 @@ def ignore_sensor(sensor: DiscoveredSensor, manager: DiscoveryManager):
|
|||||||
|
|
||||||
manager.ignore(sensor.mac, reason if reason else None)
|
manager.ignore(sensor.mac, reason if reason else None)
|
||||||
|
|
||||||
print(f"\n✅ Sensor ignored")
|
print(f"\n✅ Sensor ignored and marked in discovered_sensors.json")
|
||||||
if reason:
|
if reason:
|
||||||
print(f" Reason: {reason}")
|
print(f" Reason: {reason}")
|
||||||
|
|
||||||
|
|||||||
@@ -15,14 +15,19 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
# Determine project root and config directory
|
# Determine project root and config directory
|
||||||
# Check if running from system installation (/opt/sensorpajen) or development
|
# Check if running from system installation (/opt/sensorpajen) or development
|
||||||
if Path('/opt/sensorpajen').exists():
|
_opt_sensorpajen_exists = Path('/opt/sensorpajen').exists()
|
||||||
|
_var_lib_exists = Path('/var/lib/sensorpajen').exists()
|
||||||
|
|
||||||
|
if _opt_sensorpajen_exists:
|
||||||
# System installation
|
# System installation
|
||||||
PROJECT_ROOT = Path('/opt/sensorpajen')
|
PROJECT_ROOT = Path('/opt/sensorpajen')
|
||||||
CONFIG_DIR = Path('/etc/sensorpajen')
|
CONFIG_DIR = Path('/etc/sensorpajen')
|
||||||
|
STATE_DIR = Path('/var/lib/sensorpajen')
|
||||||
else:
|
else:
|
||||||
# Development installation (3 levels up from this file: src/sensorpajen/config.py)
|
# Development installation (3 levels up from this file: src/sensorpajen/config.py)
|
||||||
PROJECT_ROOT = Path(__file__).parent.parent.parent
|
PROJECT_ROOT = Path(__file__).parent.parent.parent
|
||||||
CONFIG_DIR = PROJECT_ROOT / "config"
|
CONFIG_DIR = PROJECT_ROOT / "config"
|
||||||
|
STATE_DIR = CONFIG_DIR
|
||||||
|
|
||||||
# MQTT Configuration from environment
|
# MQTT Configuration from environment
|
||||||
MQTT_HOST = os.environ.get("MQTT_HOST")
|
MQTT_HOST = os.environ.get("MQTT_HOST")
|
||||||
@@ -63,7 +68,7 @@ NTFY_TOKEN = os.environ.get("NTFY_TOKEN", "")
|
|||||||
# Discovery settings
|
# Discovery settings
|
||||||
DISCOVERED_SENSORS_FILE = os.environ.get(
|
DISCOVERED_SENSORS_FILE = os.environ.get(
|
||||||
"DISCOVERED_SENSORS_FILE",
|
"DISCOVERED_SENSORS_FILE",
|
||||||
str(CONFIG_DIR / "discovered_sensors.json")
|
str(STATE_DIR / "discovered_sensors.json")
|
||||||
)
|
)
|
||||||
CONFIG_RELOAD_INTERVAL = int(os.environ.get("CONFIG_RELOAD_INTERVAL", "900")) # 15 minutes
|
CONFIG_RELOAD_INTERVAL = int(os.environ.get("CONFIG_RELOAD_INTERVAL", "900")) # 15 minutes
|
||||||
|
|
||||||
@@ -85,11 +90,11 @@ class SensorConfig:
|
|||||||
def load(self):
|
def load(self):
|
||||||
"""Load sensor configuration from JSON file."""
|
"""Load sensor configuration from JSON file."""
|
||||||
if not self.config_file.exists():
|
if not self.config_file.exists():
|
||||||
raise FileNotFoundError(
|
logger.warning(
|
||||||
f"Sensor configuration file not found: {self.config_file}\n"
|
f"Sensor configuration file not found: {self.config_file}\n"
|
||||||
f"Please copy config/sensors.json.example to config/sensors.json "
|
f"Starting with no sensors - use discovery to add sensors"
|
||||||
f"and configure your sensors."
|
|
||||||
)
|
)
|
||||||
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with open(self.config_file, 'r') as f:
|
with open(self.config_file, 'r') as f:
|
||||||
@@ -138,6 +143,7 @@ def validate_config():
|
|||||||
logger.info(f"Installation Type: {install_type}")
|
logger.info(f"Installation Type: {install_type}")
|
||||||
logger.info(f"Project Root: {PROJECT_ROOT}")
|
logger.info(f"Project Root: {PROJECT_ROOT}")
|
||||||
logger.info(f"Config Directory: {CONFIG_DIR}")
|
logger.info(f"Config Directory: {CONFIG_DIR}")
|
||||||
|
logger.info(f"State Directory: {STATE_DIR}")
|
||||||
logger.info(f"MQTT Host: {MQTT_HOST}:{MQTT_PORT}")
|
logger.info(f"MQTT Host: {MQTT_HOST}:{MQTT_PORT}")
|
||||||
logger.info(f"MQTT User: {MQTT_USER}")
|
logger.info(f"MQTT User: {MQTT_USER}")
|
||||||
logger.info(f"MQTT Client ID: {MQTT_CLIENT_ID}")
|
logger.info(f"MQTT Client ID: {MQTT_CLIENT_ID}")
|
||||||
|
|||||||
@@ -125,9 +125,9 @@ class Sensorpajen:
|
|||||||
self.sensor_config = config.SensorConfig()
|
self.sensor_config = config.SensorConfig()
|
||||||
|
|
||||||
if len(self.sensor_config.sensors) == 0:
|
if len(self.sensor_config.sensors) == 0:
|
||||||
self.logger.error("No sensors configured!")
|
self.logger.warning("No sensors configured")
|
||||||
self.logger.error("Please configure sensors in config/sensors.json")
|
self.logger.warning("Starting in discovery-only mode")
|
||||||
sys.exit(1)
|
self.logger.warning("Use 'sensorpajen approve-sensors' to add sensors")
|
||||||
|
|
||||||
# Initialize discovery manager
|
# Initialize discovery manager
|
||||||
self.logger.info("Initializing discovery manager...")
|
self.logger.info("Initializing discovery manager...")
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ SyslogIdentifier=sensorpajen
|
|||||||
PrivateTmp=true
|
PrivateTmp=true
|
||||||
ProtectSystem=strict
|
ProtectSystem=strict
|
||||||
ProtectHome=true
|
ProtectHome=true
|
||||||
ReadWritePaths=/etc/sensorpajen
|
ReadWritePaths=/etc/sensorpajen /var/lib/sensorpajen
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
|
|||||||
@@ -5,9 +5,16 @@ MQTT_USER=hasse
|
|||||||
MQTT_PASSWORD=casablanca
|
MQTT_PASSWORD=casablanca
|
||||||
MQTT_CLIENT_ID=mibridge
|
MQTT_CLIENT_ID=mibridge
|
||||||
|
|
||||||
# Sensor Configuration (relative to project root)
|
# Sensor Configuration
|
||||||
SENSOR_CONFIG_FILE=config/sensors.json
|
# For system installation (/opt/sensorpajen): Use absolute paths
|
||||||
DISCOVERED_SENSORS_FILE=config/discovered_sensors.json
|
# SENSOR_CONFIG_FILE=/etc/sensorpajen/sensors.json
|
||||||
|
# DISCOVERED_SENSORS_FILE=/etc/sensorpajen/discovered_sensors.json
|
||||||
|
#
|
||||||
|
# For development installation: Use relative paths (from project root)
|
||||||
|
# SENSOR_CONFIG_FILE=config/sensors.json
|
||||||
|
# DISCOVERED_SENSORS_FILE=config/discovered_sensors.json
|
||||||
|
#
|
||||||
|
# If not set, defaults will be used based on installation type
|
||||||
|
|
||||||
# Application Settings
|
# Application Settings
|
||||||
WATCHDOG_TIMEOUT=5
|
WATCHDOG_TIMEOUT=5
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "sensorpajen"
|
name = "sensorpajen"
|
||||||
version = "2.0.0-dev"
|
version = "2.0.0"
|
||||||
description = "Bluetooth temperature sensor monitor for Xiaomi Mijia LYWSD03MMC"
|
description = "Bluetooth temperature sensor monitor for Xiaomi Mijia LYWSD03MMC"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.9"
|
requires-python = ">=3.9"
|
||||||
@@ -14,7 +14,7 @@ authors = [
|
|||||||
]
|
]
|
||||||
keywords = ["bluetooth", "temperature", "sensor", "mqtt", "raspberry-pi"]
|
keywords = ["bluetooth", "temperature", "sensor", "mqtt", "raspberry-pi"]
|
||||||
classifiers = [
|
classifiers = [
|
||||||
"Development Status :: 4 - Beta",
|
"Development Status :: 5 - Production/Stable",
|
||||||
"Intended Audience :: Developers",
|
"Intended Audience :: Developers",
|
||||||
"License :: OSI Approved :: MIT License",
|
"License :: OSI Approved :: MIT License",
|
||||||
"Programming Language :: Python :: 3",
|
"Programming Language :: Python :: 3",
|
||||||
|
|||||||
177
readme.md
177
readme.md
@@ -22,48 +22,72 @@ Raspberry Pi service that monitors Xiaomi Mijia LYWSD03MMC Bluetooth temperature
|
|||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
See [INSTALL.md](INSTALL.md) for complete installation instructions.
|
### System Installation (Debian Package - Recommended for Raspberry Pi)
|
||||||
|
|
||||||
### Quick Start
|
The easiest way to install on Raspberry Pi OS is using the pre-built Debian package:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Clone repository
|
# Download the latest release
|
||||||
git clone <repo-url> ~/sensorpajen
|
wget https://gitea.wahlberg.se/api/v1/repos/fredrik/sensorpajen/releases/download/v2.0.0/sensorpajen_2.0.0_all.deb
|
||||||
cd ~/sensorpajen
|
|
||||||
|
|
||||||
# Create and activate virtual environment
|
# Install
|
||||||
python3 -m venv .venv
|
sudo dpkg -i sensorpajen_2.0.0_all.deb
|
||||||
source .venv/bin/activate
|
|
||||||
|
|
||||||
# Install dependencies
|
|
||||||
pip install -e .
|
|
||||||
|
|
||||||
# Configure
|
# Configure
|
||||||
cp config/sensorpajen.env.example config/sensorpajen.env
|
sudo nano /etc/sensorpajen/sensorpajen.env # Edit MQTT settings
|
||||||
cp config/sensors.json.example config/sensors.json
|
sudo systemctl restart sensorpajen
|
||||||
nano config/sensorpajen.env # Edit MQTT settings
|
|
||||||
nano config/sensors.json # Edit sensor MAC addresses
|
|
||||||
|
|
||||||
# Set Bluetooth capabilities
|
# View logs
|
||||||
sudo setcap 'cap_net_raw,cap_net_admin+eip' $(readlink -f .venv/bin/python3)
|
sudo journalctl -u sensorpajen -f
|
||||||
|
|
||||||
# Install systemd service
|
|
||||||
mkdir -p ~/.config/systemd/user/
|
|
||||||
cp systemd/sensorpajen.service ~/.config/systemd/user/
|
|
||||||
systemctl --user daemon-reload
|
|
||||||
sudo loginctl enable-linger $USER
|
|
||||||
|
|
||||||
# Start service
|
|
||||||
systemctl --user enable sensorpajen
|
|
||||||
systemctl --user start sensorpajen
|
|
||||||
|
|
||||||
# Check status
|
|
||||||
systemctl --user status sensorpajen
|
|
||||||
journalctl --user -u sensorpajen -f
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The system package installs:
|
||||||
|
- Application in `/opt/sensorpajen/`
|
||||||
|
- Configuration in `/etc/sensorpajen/`
|
||||||
|
- Runtime state in `/var/lib/sensorpajen/`
|
||||||
|
- Systemd service (runs automatically)
|
||||||
|
|
||||||
|
### Development Installation
|
||||||
|
|
||||||
|
See [INSTALL.md](INSTALL.md) for complete development setup.
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
|
### Quick Setup
|
||||||
|
|
||||||
|
After installation, configure your MQTT broker and sensors:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Edit MQTT settings
|
||||||
|
sudo nano /etc/sensorpajen/sensorpajen.env
|
||||||
|
|
||||||
|
# Restart service to apply changes
|
||||||
|
sudo systemctl restart sensorpajen
|
||||||
|
```
|
||||||
|
|
||||||
|
### Approving Sensors (Discovery Workflow)
|
||||||
|
|
||||||
|
The service automatically discovers nearby Bluetooth sensors and stores them in a pending list. You approve which ones to monitor:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Start sensor discovery (if not already running)
|
||||||
|
sudo systemctl start sensorpajen
|
||||||
|
|
||||||
|
# Let it scan for a minute or two to discover sensors
|
||||||
|
sleep 120
|
||||||
|
|
||||||
|
# View discovered sensors and approve them
|
||||||
|
sudo sensorpajen approve-sensors
|
||||||
|
```
|
||||||
|
|
||||||
|
The approval CLI will:
|
||||||
|
1. Show newly discovered sensors with their current readings
|
||||||
|
2. Ask you to approve, ignore, or skip each sensor
|
||||||
|
3. Save approved sensors to `/etc/sensorpajen/sensors.json`
|
||||||
|
4. Mark their status in `/var/lib/sensorpajen/discovered_sensors.json`
|
||||||
|
|
||||||
|
When you approve a sensor, it's added to your configuration and the service automatically starts monitoring it.
|
||||||
|
|
||||||
### MQTT Settings
|
### MQTT Settings
|
||||||
|
|
||||||
Edit `config/sensorpajen.env`:
|
Edit `config/sensorpajen.env`:
|
||||||
@@ -71,7 +95,7 @@ Edit `config/sensorpajen.env`:
|
|||||||
```bash
|
```bash
|
||||||
MQTT_HOST=192.168.1.10
|
MQTT_HOST=192.168.1.10
|
||||||
MQTT_PORT=1883
|
MQTT_PORT=1883
|
||||||
MQTT_USERNAME=username
|
MQTT_USER=username
|
||||||
MQTT_PASSWORD=password
|
MQTT_PASSWORD=password
|
||||||
MQTT_CLIENT_ID=sensorpajen
|
MQTT_CLIENT_ID=sensorpajen
|
||||||
MQTT_TOPIC_PREFIX=MiTemperature2
|
MQTT_TOPIC_PREFIX=MiTemperature2
|
||||||
@@ -79,42 +103,52 @@ MQTT_TOPIC_PREFIX=MiTemperature2
|
|||||||
|
|
||||||
### Sensors
|
### Sensors
|
||||||
|
|
||||||
Edit `config/sensors.json`:
|
Sensors are automatically managed via the approval workflow. You can also manually edit `/etc/sensorpajen/sensors.json`:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
[
|
{
|
||||||
{
|
"sensors": [
|
||||||
"mac": "A4:C1:38:12:34:56",
|
{
|
||||||
"name": "Living Room"
|
"mac": "A4:C1:38:12:34:56",
|
||||||
},
|
"name": "Living Room"
|
||||||
{
|
},
|
||||||
"mac": "A4:C1:38:AB:CD:EF",
|
{
|
||||||
"name": "Bedroom"
|
"mac": "A4:C1:38:AB:CD:EF",
|
||||||
}
|
"name": "Bedroom"
|
||||||
]
|
}
|
||||||
```
|
]
|
||||||
|
}
|
||||||
|
|
||||||
## Service Management
|
## Service Management
|
||||||
|
|
||||||
See [systemd/README.md](systemd/README.md) for detailed service management instructions.
|
### System Installation (Debian Package)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Start/stop service
|
# Start/stop service
|
||||||
systemctl --user start sensorpajen
|
sudo systemctl start sensorpajen
|
||||||
systemctl --user stop sensorpajen
|
sudo systemctl stop sensorpajen
|
||||||
|
|
||||||
# Enable/disable autostart
|
# Enable/disable autostart
|
||||||
systemctl --user enable sensorpajen
|
sudo systemctl enable sensorpajen
|
||||||
systemctl --user disable sensorpajen
|
sudo systemctl disable sensorpajen
|
||||||
|
|
||||||
# View status
|
# View status
|
||||||
systemctl --user status sensorpajen
|
sudo systemctl status sensorpajen
|
||||||
|
|
||||||
# View logs
|
# View logs (live)
|
||||||
journalctl --user -u sensorpajen -f
|
sudo journalctl -u sensorpajen -f
|
||||||
journalctl --user -u sensorpajen -n 100
|
|
||||||
|
# View last 50 log lines
|
||||||
|
sudo journalctl -u sensorpajen -n 50
|
||||||
|
|
||||||
|
# Uninstall
|
||||||
|
sudo dpkg -r sensorpajen
|
||||||
|
# Note: Configuration is preserved in /etc/sensorpajen/
|
||||||
|
# To remove config: sudo rm -rf /etc/sensorpajen/
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Development Installation
|
||||||
|
|
||||||
## Flashing New Thermometers
|
## Flashing New Thermometers
|
||||||
|
|
||||||
**Important**: Flash only one thermometer at a time!
|
**Important**: Flash only one thermometer at a time!
|
||||||
@@ -158,27 +192,40 @@ sensorpajen/
|
|||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
See [INSTALL.md](INSTALL.md#troubleshooting) for detailed troubleshooting steps.
|
### System Installation (Debian Package)
|
||||||
|
|
||||||
### Quick Checks
|
**Service won't start:**
|
||||||
|
|
||||||
**Permission errors:**
|
|
||||||
```bash
|
```bash
|
||||||
sudo setcap 'cap_net_raw,cap_net_admin+eip' $(readlink -f ~/sensorpajen/.venv/bin/python3)
|
# Check what's wrong
|
||||||
systemctl --user restart sensorpajen
|
sudo journalctl -u sensorpajen -n 50
|
||||||
|
|
||||||
|
# Check configuration is valid
|
||||||
|
sudo cat /etc/sensorpajen/sensorpajen.env
|
||||||
|
|
||||||
|
# Manually test the application
|
||||||
|
sudo /opt/sensorpajen/venv/bin/python -m sensorpajen.main
|
||||||
```
|
```
|
||||||
|
|
||||||
**Service status:**
|
**MQTT connection issues:**
|
||||||
```bash
|
```bash
|
||||||
systemctl --user status sensorpajen
|
# Verify MQTT settings in the log
|
||||||
journalctl --user -u sensorpajen -f
|
sudo journalctl -u sensorpajen | grep MQTT
|
||||||
```
|
|
||||||
|
|
||||||
**MQTT test:**
|
# Test MQTT connection manually
|
||||||
```bash
|
|
||||||
mosquitto_sub -h <MQTT_HOST> -u <USER> -P <PASSWORD> -t "MiTemperature2/#" -v
|
mosquitto_sub -h <MQTT_HOST> -u <USER> -P <PASSWORD> -t "MiTemperature2/#" -v
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Sensor not found:**
|
||||||
|
```bash
|
||||||
|
# Run sensor discovery
|
||||||
|
sudo sensorpajen approve-sensors
|
||||||
|
|
||||||
|
# Check discovered sensors
|
||||||
|
sudo cat /var/lib/sensorpajen/discovered_sensors.json | jq '.'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Development Installation
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -153,8 +153,10 @@ def approve_sensor(sensor: DiscoveredSensor, manager: DiscoveryManager):
|
|||||||
print(f" Name: {name}")
|
print(f" Name: {name}")
|
||||||
print(f" Configuration will be reloaded automatically within 15 minutes")
|
print(f" Configuration will be reloaded automatically within 15 minutes")
|
||||||
|
|
||||||
# Mark as approved in discovery manager
|
# Mark as approved in discovery manager and save
|
||||||
|
print(f"\nUpdating discovery status...")
|
||||||
manager.approve(sensor.mac)
|
manager.approve(sensor.mac)
|
||||||
|
print(f"✅ Marked as approved in discovered_sensors.json")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"\n❌ Error saving to sensors.json: {e}")
|
print(f"\n❌ Error saving to sensors.json: {e}")
|
||||||
@@ -172,7 +174,7 @@ def ignore_sensor(sensor: DiscoveredSensor, manager: DiscoveryManager):
|
|||||||
|
|
||||||
manager.ignore(sensor.mac, reason if reason else None)
|
manager.ignore(sensor.mac, reason if reason else None)
|
||||||
|
|
||||||
print(f"\n✅ Sensor ignored")
|
print(f"\n✅ Sensor ignored and marked in discovered_sensors.json")
|
||||||
if reason:
|
if reason:
|
||||||
print(f" Reason: {reason}")
|
print(f" Reason: {reason}")
|
||||||
|
|
||||||
|
|||||||
@@ -15,14 +15,19 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
# Determine project root and config directory
|
# Determine project root and config directory
|
||||||
# Check if running from system installation (/opt/sensorpajen) or development
|
# Check if running from system installation (/opt/sensorpajen) or development
|
||||||
if Path('/opt/sensorpajen').exists():
|
_opt_sensorpajen_exists = Path('/opt/sensorpajen').exists()
|
||||||
|
_var_lib_exists = Path('/var/lib/sensorpajen').exists()
|
||||||
|
|
||||||
|
if _opt_sensorpajen_exists:
|
||||||
# System installation
|
# System installation
|
||||||
PROJECT_ROOT = Path('/opt/sensorpajen')
|
PROJECT_ROOT = Path('/opt/sensorpajen')
|
||||||
CONFIG_DIR = Path('/etc/sensorpajen')
|
CONFIG_DIR = Path('/etc/sensorpajen')
|
||||||
|
STATE_DIR = Path('/var/lib/sensorpajen')
|
||||||
else:
|
else:
|
||||||
# Development installation (3 levels up from this file: src/sensorpajen/config.py)
|
# Development installation (3 levels up from this file: src/sensorpajen/config.py)
|
||||||
PROJECT_ROOT = Path(__file__).parent.parent.parent
|
PROJECT_ROOT = Path(__file__).parent.parent.parent
|
||||||
CONFIG_DIR = PROJECT_ROOT / "config"
|
CONFIG_DIR = PROJECT_ROOT / "config"
|
||||||
|
STATE_DIR = CONFIG_DIR
|
||||||
|
|
||||||
# MQTT Configuration from environment
|
# MQTT Configuration from environment
|
||||||
MQTT_HOST = os.environ.get("MQTT_HOST")
|
MQTT_HOST = os.environ.get("MQTT_HOST")
|
||||||
@@ -63,7 +68,7 @@ NTFY_TOKEN = os.environ.get("NTFY_TOKEN", "")
|
|||||||
# Discovery settings
|
# Discovery settings
|
||||||
DISCOVERED_SENSORS_FILE = os.environ.get(
|
DISCOVERED_SENSORS_FILE = os.environ.get(
|
||||||
"DISCOVERED_SENSORS_FILE",
|
"DISCOVERED_SENSORS_FILE",
|
||||||
str(CONFIG_DIR / "discovered_sensors.json")
|
str(STATE_DIR / "discovered_sensors.json")
|
||||||
)
|
)
|
||||||
CONFIG_RELOAD_INTERVAL = int(os.environ.get("CONFIG_RELOAD_INTERVAL", "900")) # 15 minutes
|
CONFIG_RELOAD_INTERVAL = int(os.environ.get("CONFIG_RELOAD_INTERVAL", "900")) # 15 minutes
|
||||||
|
|
||||||
@@ -138,6 +143,7 @@ def validate_config():
|
|||||||
logger.info(f"Installation Type: {install_type}")
|
logger.info(f"Installation Type: {install_type}")
|
||||||
logger.info(f"Project Root: {PROJECT_ROOT}")
|
logger.info(f"Project Root: {PROJECT_ROOT}")
|
||||||
logger.info(f"Config Directory: {CONFIG_DIR}")
|
logger.info(f"Config Directory: {CONFIG_DIR}")
|
||||||
|
logger.info(f"State Directory: {STATE_DIR}")
|
||||||
logger.info(f"MQTT Host: {MQTT_HOST}:{MQTT_PORT}")
|
logger.info(f"MQTT Host: {MQTT_HOST}:{MQTT_PORT}")
|
||||||
logger.info(f"MQTT User: {MQTT_USER}")
|
logger.info(f"MQTT User: {MQTT_USER}")
|
||||||
logger.info(f"MQTT Client ID: {MQTT_CLIENT_ID}")
|
logger.info(f"MQTT Client ID: {MQTT_CLIENT_ID}")
|
||||||
|
|||||||
Reference in New Issue
Block a user