mirror of
https://github.com/OMGeeky/homecontrol.esp-sensors.git
synced 2026-02-23 15:49:52 +01:00
split dht sensor, add oled impl, add button triggered display example
This commit is contained in:
@@ -10,10 +10,11 @@ except ImportError:
|
||||
import random
|
||||
SIMULATION = True
|
||||
|
||||
from .sensor import Sensor
|
||||
from .temperature import TemperatureSensor
|
||||
from .humidity import HumiditySensor
|
||||
|
||||
|
||||
class DHT22Sensor(Sensor):
|
||||
class DHT22Sensor(TemperatureSensor, HumiditySensor):
|
||||
"""DHT22 temperature and humidity sensor implementation."""
|
||||
|
||||
def __init__(self, name: str, pin: int, interval: int = 60, unit: str = "C"):
|
||||
@@ -26,17 +27,15 @@ class DHT22Sensor(Sensor):
|
||||
interval: Reading interval in seconds (default: 60)
|
||||
unit: Temperature unit, either "C" for Celsius or "F" for Fahrenheit (default: "C")
|
||||
"""
|
||||
super().__init__(name, pin, interval)
|
||||
if unit not in ["C", "F"]:
|
||||
raise ValueError("Unit must be either 'C' or 'F'")
|
||||
self.unit = unit
|
||||
self._last_humidity = None
|
||||
# Initialize both parent classes
|
||||
TemperatureSensor.__init__(self, name, pin, interval, unit)
|
||||
HumiditySensor.__init__(self, name, pin, interval)
|
||||
|
||||
# Initialize the sensor if not in simulation mode
|
||||
if not SIMULATION:
|
||||
self._sensor = dht.DHT22(Pin(pin))
|
||||
|
||||
def read(self) -> float:
|
||||
def read_temperature(self) -> float:
|
||||
"""
|
||||
Read the current temperature.
|
||||
|
||||
@@ -44,14 +43,11 @@ class DHT22Sensor(Sensor):
|
||||
The temperature reading as a float
|
||||
"""
|
||||
if SIMULATION:
|
||||
# Simulate temperature reading
|
||||
if self.unit == "C":
|
||||
self._last_reading = round(random.uniform(15.0, 30.0), 1)
|
||||
else:
|
||||
self._last_reading = round(random.uniform(59.0, 86.0), 1)
|
||||
|
||||
# Simulate humidity reading (between 30% and 90%)
|
||||
self._last_humidity = round(random.uniform(30.0, 90.0), 1)
|
||||
# Use parent class simulation for temperature
|
||||
temp_reading = super().read_temperature()
|
||||
# Also update humidity in simulation mode
|
||||
self._last_humidity = super().read_humidity()
|
||||
return temp_reading
|
||||
else:
|
||||
# Actual hardware reading
|
||||
try:
|
||||
@@ -63,6 +59,7 @@ class DHT22Sensor(Sensor):
|
||||
temp = (temp * 9 / 5) + 32
|
||||
|
||||
self._last_reading = round(temp, 1)
|
||||
# Also read humidity while we're at it
|
||||
self._last_humidity = round(self._sensor.humidity(), 1)
|
||||
except Exception as e:
|
||||
print(f"Error reading DHT22 sensor: {e}")
|
||||
@@ -74,6 +71,15 @@ class DHT22Sensor(Sensor):
|
||||
|
||||
return self._last_reading
|
||||
|
||||
def read(self) -> float:
|
||||
"""
|
||||
Read the current temperature (wrapper for read_temperature).
|
||||
|
||||
Returns:
|
||||
The temperature reading as a float
|
||||
"""
|
||||
return self.read_temperature()
|
||||
|
||||
def read_humidity(self) -> float:
|
||||
"""
|
||||
Read the current humidity.
|
||||
@@ -84,8 +90,8 @@ class DHT22Sensor(Sensor):
|
||||
# If we haven't read yet, read only humidity
|
||||
if self._last_humidity is None:
|
||||
if SIMULATION:
|
||||
# Simulate humidity reading (between 30% and 90%)
|
||||
self._last_humidity = round(random.uniform(30.0, 90.0), 1)
|
||||
# Use parent class simulation
|
||||
return super().read_humidity()
|
||||
else:
|
||||
# Actual hardware reading
|
||||
try:
|
||||
@@ -104,9 +110,13 @@ class DHT22Sensor(Sensor):
|
||||
Returns:
|
||||
A dictionary containing sensor metadata
|
||||
"""
|
||||
metadata = super().get_metadata()
|
||||
metadata["unit"] = self.unit
|
||||
metadata["last_humidity"] = self._last_humidity
|
||||
# Get metadata from TemperatureSensor
|
||||
temp_metadata = TemperatureSensor.get_metadata(self)
|
||||
# Get metadata from HumiditySensor
|
||||
humidity_metadata = HumiditySensor.get_metadata(self)
|
||||
|
||||
# Combine metadata from both parent classes
|
||||
metadata = {**temp_metadata, **humidity_metadata}
|
||||
metadata["type"] = "DHT22"
|
||||
return metadata
|
||||
|
||||
|
||||
44
src/esp_sensors/humidity.py
Normal file
44
src/esp_sensors/humidity.py
Normal file
@@ -0,0 +1,44 @@
|
||||
"""
|
||||
Humidity sensor module for ESP-based sensors.
|
||||
"""
|
||||
import random
|
||||
from .sensor import Sensor
|
||||
|
||||
|
||||
class HumiditySensor(Sensor):
|
||||
"""Humidity sensor implementation."""
|
||||
|
||||
def __init__(self, name: str, pin: int, interval: int = 60):
|
||||
"""
|
||||
Initialize a new humidity sensor.
|
||||
|
||||
Args:
|
||||
name: The name of the sensor
|
||||
pin: The GPIO pin number the sensor is connected to
|
||||
interval: Reading interval in seconds (default: 60)
|
||||
"""
|
||||
super().__init__(name, pin, interval)
|
||||
self._last_humidity = None
|
||||
|
||||
def read_humidity(self) -> float:
|
||||
"""
|
||||
Read the current humidity.
|
||||
|
||||
Returns:
|
||||
The humidity reading as a float (percentage)
|
||||
"""
|
||||
# This is a simulation for testing purposes
|
||||
# In a real implementation, this would read from the actual sensor
|
||||
self._last_humidity = round(random.uniform(30.0, 90.0), 1)
|
||||
return self._last_humidity
|
||||
|
||||
def get_metadata(self):
|
||||
"""
|
||||
Get sensor metadata including humidity information.
|
||||
|
||||
Returns:
|
||||
A dictionary containing sensor metadata
|
||||
"""
|
||||
metadata = super().get_metadata()
|
||||
metadata["last_humidity"] = self._last_humidity
|
||||
return metadata
|
||||
136
src/esp_sensors/oled_display.py
Normal file
136
src/esp_sensors/oled_display.py
Normal file
@@ -0,0 +1,136 @@
|
||||
"""
|
||||
OLED display module for ESP32 using SSD1306 controller.
|
||||
"""
|
||||
import time
|
||||
try:
|
||||
from machine import Pin, I2C
|
||||
import ssd1306
|
||||
SIMULATION = False
|
||||
except ImportError:
|
||||
SIMULATION = True
|
||||
|
||||
from .sensor import Sensor
|
||||
|
||||
|
||||
class OLEDDisplay(Sensor):
|
||||
"""SSD1306 OLED display implementation."""
|
||||
|
||||
def __init__(self, name: str, scl_pin: int, sda_pin: int, width: int = 128, height: int = 64,
|
||||
address: int = 0x3C, interval: int = 60):
|
||||
"""
|
||||
Initialize a new OLED display.
|
||||
|
||||
Args:
|
||||
name: The name of the display
|
||||
scl_pin: The GPIO pin number for the SCL (clock) line
|
||||
sda_pin: The GPIO pin number for the SDA (data) line
|
||||
width: Display width in pixels (default: 128)
|
||||
height: Display height in pixels (default: 64)
|
||||
address: I2C address of the display (default: 0x3C)
|
||||
interval: Refresh interval in seconds (default: 60)
|
||||
"""
|
||||
# Use sda_pin as the pin parameter for the Sensor base class
|
||||
super().__init__(name, sda_pin, interval)
|
||||
|
||||
self.scl_pin = scl_pin
|
||||
self.sda_pin = sda_pin
|
||||
self.width = width
|
||||
self.height = height
|
||||
self.address = address
|
||||
self._values = []
|
||||
|
||||
# Initialize the display if not in simulation mode
|
||||
if not SIMULATION:
|
||||
try:
|
||||
i2c = I2C(0, scl=Pin(scl_pin), sda=Pin(sda_pin))
|
||||
self._display = ssd1306.SSD1306_I2C(width, height, i2c, addr=address)
|
||||
self._display.fill(0) # Clear the display
|
||||
self._display.text("Initialized", 0, 0, 1)
|
||||
self._display.show()
|
||||
except Exception as e:
|
||||
print(f"Error initializing OLED display: {e}")
|
||||
self._display = None
|
||||
else:
|
||||
# In simulation mode, just print to console
|
||||
print(f"Simulated OLED display initialized: {width}x{height}")
|
||||
self._display = None
|
||||
|
||||
def clear(self):
|
||||
"""
|
||||
Clear the display.
|
||||
"""
|
||||
if SIMULATION:
|
||||
print("Simulated OLED display cleared")
|
||||
else:
|
||||
if self._display:
|
||||
self._display.fill(0)
|
||||
self._display.show()
|
||||
|
||||
def display_text(self, text: str, x: int = 0, y: int = 0, color: int = 1):
|
||||
"""
|
||||
Display text at the specified position.
|
||||
|
||||
Args:
|
||||
text: The text to display
|
||||
x: X coordinate (default: 0)
|
||||
y: Y coordinate (default: 0)
|
||||
color: Pixel color (1 for white, 0 for black, default: 1)
|
||||
"""
|
||||
if SIMULATION:
|
||||
print(f"Simulated OLED display text at ({x}, {y}): {text}")
|
||||
else:
|
||||
if self._display:
|
||||
self._display.text(text, x, y, color)
|
||||
self._display.show()
|
||||
|
||||
def display_values(self, values: list):
|
||||
"""
|
||||
Display a list of values on the OLED screen.
|
||||
|
||||
Args:
|
||||
values: List of values to display (strings or objects with __str__ method)
|
||||
"""
|
||||
self._values = values
|
||||
|
||||
if SIMULATION:
|
||||
print("Simulated OLED display values:")
|
||||
for i, value in enumerate(values):
|
||||
print(f" Line {i}: {value}")
|
||||
else:
|
||||
if self._display:
|
||||
self._display.fill(0) # Clear the display
|
||||
|
||||
# Display each value on a new line (8 pixels per line)
|
||||
for i, value in enumerate(values):
|
||||
if i * 10 < self.height: # Make sure we don't go off the screen
|
||||
self._display.text(str(value), 0, i * 10, 1)
|
||||
|
||||
self._display.show()
|
||||
|
||||
def read(self) -> float:
|
||||
"""
|
||||
Update the display (placeholder to satisfy Sensor interface).
|
||||
|
||||
Returns:
|
||||
Always returns 1.0 to indicate success
|
||||
"""
|
||||
# This method is required by the Sensor interface but doesn't make sense for a display
|
||||
# We'll just return a constant value
|
||||
return 1.0
|
||||
|
||||
def get_metadata(self):
|
||||
"""
|
||||
Get display metadata.
|
||||
|
||||
Returns:
|
||||
A dictionary containing display metadata
|
||||
"""
|
||||
metadata = super().get_metadata()
|
||||
metadata["scl_pin"] = self.scl_pin
|
||||
metadata["sda_pin"] = self.sda_pin
|
||||
metadata["width"] = self.width
|
||||
metadata["height"] = self.height
|
||||
metadata["address"] = self.address
|
||||
metadata["type"] = "SSD1306"
|
||||
metadata["values_count"] = len(self._values)
|
||||
return metadata
|
||||
@@ -23,7 +23,7 @@ class TemperatureSensor(Sensor):
|
||||
raise ValueError("Unit must be either 'C' or 'F'")
|
||||
self.unit = unit
|
||||
|
||||
def read(self) -> float:
|
||||
def read_temperature(self) -> float:
|
||||
"""
|
||||
Read the current temperature.
|
||||
|
||||
@@ -38,6 +38,15 @@ class TemperatureSensor(Sensor):
|
||||
self._last_reading = round(random.uniform(59.0, 86.0), 1)
|
||||
return self._last_reading
|
||||
|
||||
def read(self) -> float:
|
||||
"""
|
||||
Read the current temperature (wrapper for read_temperature).
|
||||
|
||||
Returns:
|
||||
The temperature reading as a float
|
||||
"""
|
||||
return self.read_temperature()
|
||||
|
||||
def get_metadata(self):
|
||||
"""
|
||||
Get sensor metadata including temperature unit.
|
||||
|
||||
Reference in New Issue
Block a user