refactor config logic

This commit is contained in:
OMGeeky
2025-05-07 22:07:25 +02:00
parent 0fb0566adc
commit c5e02c1d73
15 changed files with 259 additions and 225 deletions

View File

@@ -62,28 +62,28 @@ import time
# Initialize with custom parameters
sensor = DHT22Sensor(
name="outdoor",
pin=5,
interval=30, # Read every 30 seconds
unit="F" # Use Fahrenheit
name="outdoor",
pin=5,
interval=30, # Read every 30 seconds
temperature_unit="F" # Use Fahrenheit
)
# Continuous reading
try:
while True:
temp = sensor.read()
humidity = sensor.read_humidity()
# Get metadata
metadata = sensor.get_metadata()
print(f"Sensor: {metadata['name']}")
print(f"Temperature: {temp}°F ({sensor.to_celsius()}°C)")
print(f"Humidity: {humidity}%")
time.sleep(metadata['interval'])
while True:
temp = sensor.read()
humidity = sensor.read_humidity()
# Get metadata
metadata = sensor.get_metadata()
print(f"Sensor: {metadata['name']}")
print(f"Temperature: {temp}°F ({sensor.to_celsius()}°C)")
print(f"Humidity: {humidity}%")
time.sleep(metadata['interval'])
except KeyboardInterrupt:
print("Monitoring stopped")
print("Monitoring stopped")
```
## API Reference

View File

@@ -7,21 +7,28 @@ This example demonstrates how to:
3. Wake up and read sensor data when the button is pressed
4. Display the data on an OLED screen
"""
import time
import sys
import os
# Add the src directory to the Python path
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
from src.esp_sensors.oled_display import OLEDDisplay
from src.esp_sensors.dht22 import DHT22Sensor
from src.esp_sensors.config import load_config, get_sensor_config, get_display_config, get_button_config
from src.esp_sensors.config import (
load_config,
get_sensor_config,
get_display_config,
get_button_config,
)
# Import hardware-specific modules if available (for ESP32/ESP8266)
try:
from machine import Pin, deepsleep
import esp32
SIMULATION = False
except ImportError:
# Simulation mode for development on non-ESP hardware
@@ -31,10 +38,12 @@ except ImportError:
def simulate_button_press():
"""Simulate a button press in simulation mode."""
print("\nPress Enter to simulate a button press (or 'q' to quit, Ctrl+C to exit)...")
print(
"\nPress Enter to simulate a button press (or 'q' to quit, Ctrl+C to exit)..."
)
try:
user_input = input()
if user_input.lower() == 'q':
if user_input.lower() == "q":
return False
return True
except KeyboardInterrupt:
@@ -49,16 +58,14 @@ def main():
config = load_config()
# Initialize a DHT22 sensor using configuration
dht_sensor = DHT22Sensor(
config=config # Pass the loaded config
)
dht_sensor = DHT22Sensor(sensor_config=config) # Pass the loaded config
print(f"Initialized DHT22 sensor: {dht_sensor.name}, pin: {dht_sensor.pin}")
# Initialize an OLED display using configuration
display = OLEDDisplay(
config=config # Pass the loaded config
display = OLEDDisplay(config=config) # Pass the loaded config
print(
f"Initialized OLED display: {display.name}, size: {display.width}x{display.height}"
)
print(f"Initialized OLED display: {display.name}, size: {display.width}x{display.height}")
# Set up button using configuration
button_config = get_button_config("main_button", config)
@@ -88,7 +95,9 @@ def main():
# Go to light sleep mode to save power
# Wake up on pin change (button press)
print("Entering light sleep mode...")
esp32.wake_on_ext0(pin=button, level=0) # Wake on button press (low)
esp32.wake_on_ext0(
pin=button, level=0
) # Wake on button press (low)
esp32.light_sleep() # Light sleep preserves RAM but saves power
# When we get here, the button was pressed
@@ -105,13 +114,9 @@ def main():
name_str = f"Sensor: {dht_sensor.name}"
# Display values
display.display_values([
name_str,
temp_str,
hum_str,
time_str,
"Press button again"
])
display.display_values(
[name_str, temp_str, hum_str, time_str, "Press button again"]
)
# Print to console
print(f"Updated display with: {temp_str}, {hum_str}")

View File

@@ -9,15 +9,16 @@ Usage:
- Ensure config.json is properly set up with DHT22 sensor configuration
- The script will read temperature and humidity at the specified interval
"""
import time
import sys
import os
# Add the src directory to the Python path if needed
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
# Check if running on MicroPython
if sys.implementation.name == 'micropython':
if sys.implementation.name == "micropython":
from src.esp_sensors.dht22 import DHT22Sensor
from src.esp_sensors.config import load_config, get_sensor_config
@@ -31,7 +32,7 @@ if sys.implementation.name == 'micropython':
print(f"Sensor pin: {dht_config.get('pin')}")
# Initialize the sensor using configuration
sensor = DHT22Sensor(config=config)
sensor = DHT22Sensor(sensor_config=config)
print("Starting sensor readings. Press Ctrl+C to stop.")
@@ -79,7 +80,7 @@ else:
print(f"Sensor pin: {dht_config.get('pin')}")
# Initialize the sensor using configuration
sensor = DHT22Sensor(config=config)
sensor = DHT22Sensor(sensor_config=config)
print("Starting simulated sensor readings. Press Ctrl+C to stop.")

View File

@@ -2,12 +2,13 @@
Example usage of the OLED display with temperature and humidity sensors.
This example demonstrates how to use the configuration system to initialize sensors and displays.
"""
import time
import sys
import os
# Add the src directory to the Python path
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
from src.esp_sensors.oled_display import OLEDDisplay
from src.esp_sensors.dht22 import DHT22Sensor
@@ -31,10 +32,12 @@ def main():
# Initialize a DHT22 sensor with configuration
dht_sensor = DHT22Sensor(
sensor_type="dht22", # This will load config for this sensor type
config=config # Pass the loaded config
# This will load config for this sensor type
sensor_config=config, # Pass the loaded config
)
print(
f"Created DHT22 sensor: {dht_sensor.name}, pin: {dht_sensor.pin}, interval: {dht_sensor.interval}s"
)
print(f"Created DHT22 sensor: {dht_sensor.name}, pin: {dht_sensor.pin}, interval: {dht_sensor.interval}s")
# Method 2: Initialize with some parameters from config, others specified directly
print("\nMethod 2: Override some config parameters")
@@ -44,11 +47,12 @@ def main():
# These parameters will override the config values
name="Custom Display",
interval=1, # Update every second
# Other parameters will be loaded from config
config=config
config=config,
)
print(
f"Created OLED display: {display.name}, size: {display.width}x{display.height}, interval: {display.interval}s"
)
print(f"Created OLED display: {display.name}, size: {display.width}x{display.height}, interval: {display.interval}s")
# Display initialization message
display.clear()
@@ -72,13 +76,9 @@ def main():
name_str = f"Sensor: {dht_sensor.name}"
# Display values
display.display_values([
name_str,
temp_str,
hum_str,
time_str,
f"Demo ({i+1}/5)"
])
display.display_values(
[name_str, temp_str, hum_str, time_str, f"Demo ({i+1}/5)"]
)
# Print to console in simulation mode
print(f"Updated display with: {temp_str}, {hum_str}")

View File

@@ -5,6 +5,7 @@ This module provides functionality to load and save configuration settings
from/to a file, making it easy to change parameters like pins, display resolution,
sensor names, and intervals without modifying the code.
"""
import json
import os
from typing import Dict, Any, Optional, Union, List
@@ -15,28 +16,12 @@ DEFAULT_CONFIG_PATH = "config.json"
# Default configuration values
DEFAULT_CONFIG = {
"sensors": {
"temperature": {
"name": "Temperature Sensor",
"pin": 4,
"interval": 60,
"unit": "C"
},
"humidity": {
"name": "Humidity Sensor",
"pin": 4,
"interval": 60
},
"dht22": {
"name": "DHT22 Sensor",
"pin": 4,
"interval": 60,
"temperature": {
"name": "DHT22 Temperature",
"unit": "C"
},
"humidity": {
"name": "DHT22 Humidity"
}
"temperature": {"name": "DHT22 Temperature", "unit": "C"},
"humidity": {"name": "DHT22 Humidity"},
}
},
"displays": {
@@ -47,15 +32,10 @@ DEFAULT_CONFIG = {
"width": 128,
"height": 64,
"address": "0x3C",
"interval": 1
"interval": 1,
}
},
"buttons": {
"main_button": {
"pin": 0,
"pull_up": True
}
}
"buttons": {"main_button": {"pin": 0, "pull_up": True}},
}
@@ -73,11 +53,13 @@ def load_config(config_path: str = DEFAULT_CONFIG_PATH) -> Dict[str, Any]:
"""
try:
if os.path.exists(config_path):
with open(config_path, 'r') as f:
with open(config_path, "r") as f:
config = json.load(f)
return config
else:
print(f"Configuration file {config_path} not found. Using default configuration.")
print(
f"Configuration file {config_path} not found. Using default configuration."
)
return DEFAULT_CONFIG
except Exception as e:
print(f"Error loading configuration: {e}. Using default configuration.")
@@ -96,7 +78,7 @@ def save_config(config: Dict[str, Any], config_path: str = DEFAULT_CONFIG_PATH)
True if the configuration was saved successfully, False otherwise
"""
try:
with open(config_path, 'w') as f:
with open(config_path, "w") as f:
json.dump(config, f, indent=4)
return True
except Exception as e:
@@ -104,7 +86,9 @@ def save_config(config: Dict[str, Any], config_path: str = DEFAULT_CONFIG_PATH)
return False
def get_sensor_config(sensor_type: str, config: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
def get_sensor_config(
sensor_type: str, config: Optional[Dict[str, Any]] = None
) -> Dict[str, Any]:
"""
Get configuration for a specific sensor type.
@@ -126,7 +110,9 @@ def get_sensor_config(sensor_type: str, config: Optional[Dict[str, Any]] = None)
return sensor_config
def get_display_config(display_type: str, config: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
def get_display_config(
display_type: str, config: Optional[Dict[str, Any]] = None
) -> Dict[str, Any]:
"""
Get configuration for a specific display type.
@@ -148,7 +134,9 @@ def get_display_config(display_type: str, config: Optional[Dict[str, Any]] = Non
return display_config
def get_button_config(button_name: str = "main_button", config: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
def get_button_config(
button_name: str = "main_button", config: Optional[Dict[str, Any]] = None
) -> Dict[str, Any]:
"""
Get configuration for a specific button.
@@ -170,8 +158,6 @@ def get_button_config(button_name: str = "main_button", config: Optional[Dict[st
return button_config
def create_default_config(config_path: str = DEFAULT_CONFIG_PATH) -> bool:
"""
Create a default configuration file.

View File

@@ -1,14 +1,18 @@
"""
DHT22 temperature and humidity sensor module for ESP32.
"""
import time
from typing import Dict, Any, Optional
try:
import dht
from machine import Pin
SIMULATION = False
except ImportError:
import random
SIMULATION = True
from .temperature import TemperatureSensor
@@ -19,8 +23,14 @@ from .config import get_sensor_config
class DHT22Sensor(TemperatureSensor, HumiditySensor):
"""DHT22 temperature and humidity sensor implementation."""
def __init__(self, name: str = None, pin: int = None, interval: int = None,
unit: str = None, sensor_type: str = "dht22", config: Dict[str, Any] = None):
def __init__(
self,
name: str = None,
pin: int = None,
interval: int = None,
temperature_unit: str = None,
sensor_config: Dict[str, Any] = None,
):
"""
Initialize a new DHT22 sensor.
@@ -28,38 +38,47 @@ class DHT22Sensor(TemperatureSensor, HumiditySensor):
name: The name of the sensor (if None, loaded from config)
pin: The GPIO pin number the sensor is connected to (if None, loaded from config)
interval: Reading interval in seconds (if None, loaded from config)
unit: Temperature unit, either "C" or "F" (if None, loaded from config)
sensor_type: Type of the sensor for loading config (default: 'dht22')
config: Configuration dictionary (if provided, used instead of loading from file)
temperature_unit: Temperature unit, either "C" or "F" (if None, loaded from config)
sensor_config: Sensor-Configuration dictionary (if provided, used instead of loading from file)
"""
# Load configuration
sensor_config = get_sensor_config(sensor_type, config)
if sensor_config is None:
sensor_config = {}
# Get main parameters from config if not provided
self.name = name if name is not None else sensor_config.get('name', 'DHT22 Sensor')
self.pin = pin if pin is not None else sensor_config.get('pin', 0)
self.interval = interval if interval is not None else sensor_config.get('interval', 60)
self.apply_parameters(interval, name, pin, sensor_config)
# Get temperature configuration
temp_config = sensor_config.get('temperature', {})
temp_name = temp_config.get('name', self.name + ' Temperature')
temp_unit = unit if unit is not None else temp_config.get('unit', 'C')
# Get humidity configuration
humidity_config = sensor_config.get('humidity', {})
humidity_name = humidity_config.get('name', self.name + ' Humidity')
# Get sensor configurations
temp_config = sensor_config.get("temperature", {})
humidity_config = sensor_config.get("humidity", {})
# Initialize both parent classes
TemperatureSensor.__init__(self, name=temp_name, pin=self.pin, interval=self.interval, unit=temp_unit)
HumiditySensor.__init__(self, name=humidity_name, pin=self.pin, interval=self.interval)
TemperatureSensor.__init__(
self,
pin=self.pin,
interval=self.interval,
sensor_config=temp_config,
unit=temperature_unit,
)
HumiditySensor.__init__(
self, sensor_config=humidity_config, pin=self.pin, interval=self.interval
)
# Store the original sensor name (it gets overwritten by HumiditySensor.__init__)
self.name = name if name is not None else sensor_config.get('name', 'DHT22 Sensor')
# Re-apply parameters to ensure they are not overridden by parent classes
self.apply_parameters(interval, name, pin, sensor_config)
# Initialize the sensor if not in simulation mode
if not SIMULATION:
self._sensor = dht.DHT22(Pin(pin))
def apply_parameters(self, interval, name, pin, sensor_config):
# Get main parameters from config if not provided
self.name = (
name if name is not None else sensor_config.get("name", "DHT22 Sensor")
)
self.pin = pin if pin is not None else sensor_config.get("pin", 0)
self.interval = (
interval if interval is not None else sensor_config.get("interval", 60)
)
def read_temperature(self) -> float:
"""
Read the current temperature.

View File

@@ -1,6 +1,7 @@
"""
Humidity sensor module for ESP-based sensors.
"""
import random
from typing import Dict, Any, Optional
from .sensor import Sensor
@@ -10,8 +11,14 @@ from .config import get_sensor_config
class HumiditySensor(Sensor):
"""Humidity sensor implementation."""
def __init__(self, name: str = None, pin: int = None, interval: int = None,
sensor_type: str = None, config: Dict[str, Any] = None, **kwargs):
def __init__(
self,
name: str = None,
pin: int = None,
interval: int = None,
sensor_config: Dict[str, Any] = None,
**kwargs,
):
"""
Initialize a new humidity sensor.
@@ -19,12 +26,13 @@ class HumiditySensor(Sensor):
name: The name of the sensor (if None, loaded from config)
pin: The GPIO pin number the sensor is connected to (if None, loaded from config)
interval: Reading interval in seconds (if None, loaded from config)
sensor_type: Type of the sensor for loading config (e.g., 'humidity')
config: Configuration dictionary (if provided, used instead of loading from file)
sensor_config: Sensor-Configuration dictionary (if provided, used instead of loading from file)
**kwargs: Additional keyword arguments to pass to the parent class
"""
if sensor_config is None:
sensor_config = {}
# Initialize base class with sensor_type for configuration loading
super().__init__(name, pin, interval, sensor_type=sensor_type or "humidity", config=config)
super().__init__(name, pin, interval, sensor_config=sensor_config)
self._last_humidity = None
def read_humidity(self) -> float:

View File

@@ -1,11 +1,14 @@
"""
OLED display module for ESP32 using SSD1306 controller.
"""
import time
from typing import Dict, Any, Optional
try:
from machine import Pin, I2C
import ssd1306
SIMULATION = False
except ImportError:
SIMULATION = True
@@ -17,9 +20,17 @@ from .config import get_display_config
class OLEDDisplay(Sensor):
"""SSD1306 OLED display implementation."""
def __init__(self, name: str = None, scl_pin: int = None, sda_pin: int = None,
width: int = None, height: int = None, address: str = None,
interval: int = None, config: Dict[str, Any] = None):
def __init__(
self,
name: str = None,
scl_pin: int = None,
sda_pin: int = None,
width: int = None,
height: int = None,
address: int | str = None,
interval: int = None,
display_config: Dict[str, Any] = None,
):
"""
Initialize a new OLED display.
@@ -31,21 +42,26 @@ class OLEDDisplay(Sensor):
height: Display height in pixels (if None, loaded from config)
address: I2C address of the display (if None, loaded from config)
interval: Refresh interval in seconds (if None, loaded from config)
config: Configuration dictionary (if provided, used instead of loading from file)
display_config: Configuration dictionary
"""
# Load configuration if needed
display_config = get_display_config("oled", config)
if display_config is None:
display_config = {}
# Get parameters from config if not provided
name = name if name is not None else display_config.get("name", "OLED Display")
sda_pin = sda_pin if sda_pin is not None else display_config.get("sda_pin", 21)
interval = interval if interval is not None else display_config.get("interval", 60)
interval = (
interval if interval is not None else display_config.get("interval", 60)
)
# Initialize base class with the pin parameter
super().__init__(name=name, pin=sda_pin, interval=interval)
# Set display-specific parameters
self.scl_pin = scl_pin if scl_pin is not None else display_config.get("scl_pin", 22)
self.scl_pin = (
scl_pin if scl_pin is not None else display_config.get("scl_pin", 22)
)
self.sda_pin = sda_pin # Already set above
self.width = width if width is not None else display_config.get("width", 128)
self.height = height if height is not None else display_config.get("height", 64)

View File

@@ -1,6 +1,7 @@
"""
Base sensor module for ESP-based sensors.
"""
from typing import Dict, Any, Optional
from .config import get_sensor_config
@@ -8,8 +9,13 @@ from .config import get_sensor_config
class Sensor:
"""Base class for all sensors."""
def __init__(self, name: str = None, pin: int = None, interval: int = None,
sensor_type: str = None, config: Dict[str, Any] = None):
def __init__(
self,
name: str = None,
pin: int = None,
interval: int = None,
sensor_config: Dict[str, Any] = None,
):
"""
Initialize a new sensor.
@@ -17,22 +23,19 @@ class Sensor:
name: The name of the sensor (if None, loaded from config)
pin: The GPIO pin number the sensor is connected to (if None, loaded from config)
interval: Reading interval in seconds (if None, loaded from config)
sensor_type: Type of the sensor for loading config (e.g., 'temperature')
config: Configuration dictionary (if provided, used instead of loading from file)
sensor_config: Sensor-Configuration dictionary (if provided, used instead of loading from file)
"""
# Load configuration if sensor_type is provided
if sensor_type:
sensor_config = get_sensor_config(sensor_type, config)
# Use provided values or fall back to config values
self.name = name if name is not None else sensor_config.get('name', 'Unnamed Sensor')
self.pin = pin if pin is not None else sensor_config.get('pin', 0)
self.interval = interval if interval is not None else sensor_config.get('interval', 60)
else:
# Use provided values or defaults
self.name = name if name is not None else 'Unnamed Sensor'
self.pin = pin if pin is not None else 0
self.interval = interval if interval is not None else 60
if sensor_config is None:
sensor_config = {}
self.name = (
name if name is not None else sensor_config.get("name", "Unnamed Sensor")
)
self.pin = pin if pin is not None else sensor_config.get("pin", 0)
self.interval = (
interval if interval is not None else sensor_config.get("interval", 60)
)
self._last_reading: Optional[float] = None

View File

@@ -1,6 +1,7 @@
"""
Temperature sensor module for ESP-based sensors.
"""
import random
from typing import Dict, Any, Optional
from .sensor import Sensor
@@ -10,8 +11,14 @@ from .config import get_sensor_config
class TemperatureSensor(Sensor):
"""Temperature sensor implementation."""
def __init__(self, name: str = None, pin: int = None, interval: int = None,
unit: str = None, config: Dict[str, Any] = None):
def __init__(
self,
name: str = None,
pin: int = None,
interval: int = None,
unit: str = None,
sensor_config: Dict[str, Any] = None,
):
"""
Initialize a new temperature sensor.
@@ -20,14 +27,15 @@ class TemperatureSensor(Sensor):
pin: The GPIO pin number the sensor is connected to (if None, loaded from config)
interval: Reading interval in seconds (if None, loaded from config)
unit: Temperature unit, either "C" or "F" (if None, loaded from config)
config: Configuration dictionary (if provided, used instead of loading from file)
sensor_config: Sensor-Configuration dictionary (if provided, used instead of loading from file)
"""
if sensor_config is None:
sensor_config = {}
# Initialize base class with sensor_type for configuration loading
super().__init__(name, pin, interval, sensor_type="temperature", config=config)
super().__init__(name, pin, interval, sensor_config=sensor_config)
# Load configuration if not provided in parameters
if unit is None:
sensor_config = get_sensor_config("temperature", config)
unit = sensor_config.get("unit", "C")
# Validate unit

View File

@@ -7,17 +7,24 @@ This program:
3. Wakes up and reads sensor data when the button is pressed
4. Displays the data on an OLED screen
"""
import time
import sys
from esp_sensors.oled_display import OLEDDisplay
from esp_sensors.dht22 import DHT22Sensor
from esp_sensors.config import load_config, get_button_config
from esp_sensors.config import (
load_config,
get_button_config,
get_sensor_config,
get_display_config,
)
# Import hardware-specific modules if available (for ESP32/ESP8266)
try:
from machine import Pin, deepsleep
import esp32
SIMULATION = False
except ImportError:
# Simulation mode for development on non-ESP hardware
@@ -27,10 +34,12 @@ except ImportError:
def simulate_button_press():
"""Simulate a button press in simulation mode."""
print("\nPress Enter to simulate a button press (or 'q' to quit, Ctrl+C to exit)...")
print(
"\nPress Enter to simulate a button press (or 'q' to quit, Ctrl+C to exit)..."
)
try:
user_input = input()
if user_input.lower() == 'q':
if user_input.lower() == "q":
return False
return True
except KeyboardInterrupt:
@@ -47,12 +56,12 @@ def main():
# Initialize a DHT22 sensor using configuration
dht_sensor = DHT22Sensor(
config=config # Pass the loaded config
sensor_config=get_sensor_config("dht22", config) # Pass the loaded config
)
# Initialize an OLED display using configuration
display = OLEDDisplay(
config=config # Pass the loaded config
display_config=get_display_config("oled", config) # Pass the loaded config
)
# Set up button using configuration
@@ -80,7 +89,9 @@ def main():
# Go to light sleep mode to save power
# Wake up on pin change (button press)
print("Entering light sleep mode...")
esp32.wake_on_ext0(pin=button, level=0) # Wake on button press (low)
esp32.wake_on_ext0(
pin=button, level=0
) # Wake on button press (low)
esp32.light_sleep() # Light sleep preserves RAM but saves power
# When we get here, the button was pressed
@@ -97,13 +108,9 @@ def main():
name_str = f"Sensor: {dht_sensor.name}"
# Display values
display.display_values([
name_str,
temp_str,
hum_str,
time_str,
"Press button again"
])
display.display_values(
[name_str, temp_str, hum_str, time_str, "Press button again"]
)
# Print to console
print(f"Updated display with: {temp_str}, {hum_str}")

View File

@@ -1,17 +1,18 @@
"""
Tests for the configuration module.
"""
import os
import json
import tempfile
import pytest
from src.esp_sensors.config import (
load_config,
save_config,
get_sensor_config,
load_config,
save_config,
get_sensor_config,
get_display_config,
create_default_config,
DEFAULT_CONFIG
DEFAULT_CONFIG,
)
@@ -19,7 +20,7 @@ def test_load_default_config():
"""Test that default configuration is loaded when file doesn't exist."""
# Use a non-existent file path
config = load_config("non_existent_file.json")
# Check that the default configuration was loaded
assert config == DEFAULT_CONFIG
assert "sensors" in config
@@ -31,32 +32,28 @@ def test_save_and_load_config():
# Create a temporary file
with tempfile.NamedTemporaryFile(suffix=".json", delete=False) as temp_file:
temp_path = temp_file.name
try:
# Create a test configuration
test_config = {
"sensors": {
"test_sensor": {
"name": "Test Sensor",
"pin": 10,
"interval": 30
}
"test_sensor": {"name": "Test Sensor", "pin": 10, "interval": 30}
}
}
# Save the configuration
result = save_config(test_config, temp_path)
assert result is True
# Load the configuration
loaded_config = load_config(temp_path)
# Check that the loaded configuration matches the saved one
assert loaded_config == test_config
assert loaded_config["sensors"]["test_sensor"]["name"] == "Test Sensor"
assert loaded_config["sensors"]["test_sensor"]["pin"] == 10
assert loaded_config["sensors"]["test_sensor"]["interval"] == 30
finally:
# Clean up the temporary file
if os.path.exists(temp_path):
@@ -67,21 +64,15 @@ def test_get_sensor_config():
"""Test getting sensor configuration."""
# Create a test configuration
test_config = {
"sensors": {
"test_sensor": {
"name": "Test Sensor",
"pin": 10,
"interval": 30
}
}
"sensors": {"test_sensor": {"name": "Test Sensor", "pin": 10, "interval": 30}}
}
# Get configuration for an existing sensor
sensor_config = get_sensor_config("test_sensor", test_config)
assert sensor_config["name"] == "Test Sensor"
assert sensor_config["pin"] == 10
assert sensor_config["interval"] == 30
# Get configuration for a non-existent sensor (should return default or empty dict)
non_existent_config = get_sensor_config("non_existent", test_config)
assert isinstance(non_existent_config, dict)
@@ -92,20 +83,16 @@ def test_get_display_config():
# Create a test configuration
test_config = {
"displays": {
"test_display": {
"name": "Test Display",
"width": 64,
"height": 32
}
"test_display": {"name": "Test Display", "width": 64, "height": 32}
}
}
# Get configuration for an existing display
display_config = get_display_config("test_display", test_config)
assert display_config["name"] == "Test Display"
assert display_config["width"] == 64
assert display_config["height"] == 32
# Get configuration for a non-existent display (should return default or empty dict)
non_existent_config = get_display_config("non_existent", test_config)
assert isinstance(non_existent_config, dict)
@@ -116,25 +103,25 @@ def test_create_default_config():
# Create a temporary file path
with tempfile.NamedTemporaryFile(suffix=".json", delete=False) as temp_file:
temp_path = temp_file.name
try:
# Remove the file (we just want the path)
os.unlink(temp_path)
# Create the default configuration
result = create_default_config(temp_path)
assert result is True
# Check that the file exists
assert os.path.exists(temp_path)
# Load the configuration and check it matches the default
with open(temp_path, 'r') as f:
with open(temp_path, "r") as f:
config = json.load(f)
assert config == DEFAULT_CONFIG
finally:
# Clean up the temporary file
if os.path.exists(temp_path):
os.unlink(temp_path)
os.unlink(temp_path)

View File

@@ -1,6 +1,7 @@
"""
Tests for the DHT22 sensor module.
"""
import pytest
from src.esp_sensors.dht22 import DHT22Sensor
from src.esp_sensors.config import get_sensor_config
@@ -9,7 +10,7 @@ from src.esp_sensors.config import get_sensor_config
def test_dht22_sensor_initialization():
"""Test that a DHT22 sensor can be initialized with valid parameters."""
# Test direct parameter initialization
sensor = DHT22Sensor(name="test_sensor", pin=5, interval=30, unit="C")
sensor = DHT22Sensor(name="test_sensor", pin=5, interval=30, temperature_unit="C")
assert sensor.name == "test_sensor"
assert sensor.pin == 5
assert sensor.interval == 30
@@ -18,23 +19,14 @@ def test_dht22_sensor_initialization():
# Test initialization with custom config
test_config = {
"sensors": {
"dht22": {
"name": "config_sensor",
"pin": 6,
"interval": 40,
"temperature": {
"name": "Config Temperature",
"unit": "F"
},
"humidity": {
"name": "Config Humidity"
}
}
}
"name": "config_sensor",
"pin": 6,
"interval": 40,
"temperature": {"name": "Config Temperature", "unit": "F"},
"humidity": {"name": "Config Humidity"},
}
config_sensor = DHT22Sensor(config=test_config)
config_sensor = DHT22Sensor(sensor_config=test_config)
assert config_sensor.name == "config_sensor"
assert config_sensor.pin == 6
assert config_sensor.interval == 40
@@ -45,7 +37,7 @@ def test_dht22_sensor_initialization():
def test_dht22_sensor_invalid_unit():
"""Test that initializing with an invalid unit raises a ValueError."""
with pytest.raises(ValueError):
DHT22Sensor(name="test_sensor", pin=5, interval=30, unit="K")
DHT22Sensor(name="test_sensor", pin=5, interval=30, temperature_unit="K")
def test_dht22_sensor_read_temperature():
@@ -68,7 +60,7 @@ def test_dht22_sensor_read_humidity():
def test_dht22_sensor_fahrenheit():
"""Test that a sensor initialized with Fahrenheit returns appropriate values."""
sensor = DHT22Sensor(name="test_sensor", pin=5, unit="F")
sensor = DHT22Sensor(name="test_sensor", pin=5, temperature_unit="F")
reading = sensor.read()
assert isinstance(reading, float)
# For Fahrenheit, the reading should be between 59.0 and 86.0
@@ -78,13 +70,13 @@ def test_dht22_sensor_fahrenheit():
def test_dht22_temperature_conversion():
"""Test temperature conversion methods."""
# Test Celsius to Fahrenheit
c_sensor = DHT22Sensor(name="celsius_sensor", pin=5, unit="C")
c_sensor = DHT22Sensor(name="celsius_sensor", pin=5, temperature_unit="C")
c_sensor._last_reading = 20.0 # Manually set for testing
f_value = c_sensor.to_fahrenheit()
assert f_value == 68.0 # 20°C = 68°F
# Test Fahrenheit to Celsius
f_sensor = DHT22Sensor(name="fahrenheit_sensor", pin=5, unit="F")
f_sensor = DHT22Sensor(name="fahrenheit_sensor", pin=5, temperature_unit="F")
f_sensor._last_reading = 68.0 # Manually set for testing
c_value = f_sensor.to_celsius()
assert c_value == 20.0 # 68°F = 20°C
@@ -92,7 +84,7 @@ def test_dht22_temperature_conversion():
def test_dht22_metadata():
"""Test that metadata includes the temperature unit, humidity, and type."""
sensor = DHT22Sensor(name="test_sensor", pin=5, unit="C")
sensor = DHT22Sensor(name="test_sensor", pin=5, temperature_unit="C")
metadata = sensor.get_metadata()
assert metadata["name"] == "test_sensor"
assert metadata["pin"] == 5

View File

@@ -1,6 +1,7 @@
"""
Tests for the OLED display module.
"""
import pytest
from src.esp_sensors.oled_display import OLEDDisplay
@@ -15,20 +16,20 @@ def test_oled_display_initialization():
assert display.width == 128
assert display.height == 64
assert display.address == 0x3C
assert display.interval == 1 # Default interval is now 1 in the configuration
assert display.interval == 60
assert display._values == []
def test_oled_display_custom_parameters():
"""Test that an OLED display can be initialized with custom parameters."""
display = OLEDDisplay(
"custom_display",
scl_pin=22,
sda_pin=21,
width=64,
height=32,
address=0x3D,
interval=30
"custom_display",
scl_pin=22,
sda_pin=21,
width=64,
height=32,
address=0x3D,
interval=30,
)
assert display.name == "custom_display"
assert display.scl_pin == 22
@@ -57,7 +58,7 @@ def test_oled_display_metadata():
assert metadata["width"] == 128
assert metadata["height"] == 64
assert metadata["address"] == 0x3C
assert metadata["interval"] == 1 # Default interval is now 1 in the configuration
assert metadata["interval"] == 60
assert metadata["type"] == "SSD1306"
assert metadata["values_count"] == 0

View File

@@ -1,6 +1,7 @@
"""
Tests for the temperature sensor module.
"""
import pytest
from src.esp_sensors.temperature import TemperatureSensor