Hi,
I'm trying to get my m5paper up and running with a basic app to toggle a device.
The code displays temperature, humidity, co2, IAQ Level all correctly, but the touch interaction with the device is not working - any tips? Also, am I correctly implementing the sleep?
Thanks so much in advance!
D
import os, sys, io 
import M5
from M5 import *
import requests2
import time
import json
# Global data variable
data = {}
data['grow_lamp_state'] = "Unknown"  # To store current state
# Configuration
ha_url = "xxx"  # Change this to your HA instance
ha_token = "xxx"
screenTitle = 'Office Environment'  # Title to display
wakeup_interval = 300  # 5 minutes in seconds
# Button positioning
button_x = 270  # Center X position of button
button_y = 480  # Y position for button (adjust as needed)
button_width = 200  # Width of button
button_height = 60  # Height of button
# Define constants for e-ink (black and white)
WHITE = 0xffffff
BLACK = 0x000000
def fetchHomeAssistantData(entity_id):
    url = f"{ha_url}/api/states/{entity_id}"
    headers = {
        'Authorization': f'Bearer {ha_token}',
        'Content-Type': 'application/json'
    }
    
    try:
        response = requests2.get(url, headers=headers)
        if response.status_code == 200:
            return json.loads(response.text)
        else:
            print(f"Error: {response.status_code}")
            return None
    except Exception as e:
        print(f"Error fetching data: {e}")
        return None
def toggleGrowLamp():
    url = f"{ha_url}/api/services/light/toggle"
    headers = {
        'Authorization': f'Bearer {ha_token}',
        'Content-Type': 'application/json'
    }
    payload = {
        "entity_id": "light.grow_lamp"
    }
    
    try:
        response = requests2.post(url, headers=headers, data=json.dumps(payload))
        if response.status_code == 200:
            print("Toggled grow lamp successfully")
            # Update our stored state (will be refreshed next time)
            data['grow_lamp_state'] = "on" if data['grow_lamp_state'] == "off" else "off"
            # Force a re-render
            render()
            return True
        else:
            print(f"Error toggling grow lamp: {response.status_code}")
            return False
    except Exception as e:
        print(f"Error toggling grow lamp: {e}")
        return False
def aggregateData():
    global data
    # Device data
    try:
        data['batteryPercentage'] = str(M5.Power.getBatteryLevel()) + '%'
        data['voltage'] = f"{M5.Power.getBatVoltage() / 1000:.2f}V"
    except:
        data['batteryPercentage'] = "N/A"
        data['voltage'] = "N/A"
    
    # Check WiFi
    try:
        data['isWifiConnected'] = True  # Assume connected
    except:
        data['isWifiConnected'] = True
    
    data['currentTime'] = time.localtime()
    
    # Fetch Home Assistant data
    try:
        # Temperature from Aranet4
        temp_data = fetchHomeAssistantData("sensor.aranet4_2836d_temperature")
        if temp_data:
            data['ha_temperature'] = temp_data.get("state", "Unknown")
            data['ha_temperature_unit'] = temp_data.get("attributes", {}).get("unit_of_measurement", "°C")
        else:
            data['ha_temperature'] = "Error"
            data['ha_temperature_unit'] = ""
        
        # Humidity from Aranet4
        humidity_data = fetchHomeAssistantData("sensor.aranet4_2836d_humidity")
        if humidity_data:
            data['ha_humidity'] = humidity_data.get("state", "Unknown")
            data['ha_humidity_unit'] = humidity_data.get("attributes", {}).get("unit_of_measurement", "%")
        else:
            data['ha_humidity'] = "Error"
            data['ha_humidity_unit'] = ""
        
        # CO2 from Aranet4
        co2_data = fetchHomeAssistantData("sensor.aranet4_2836d_carbon_dioxide")
        if co2_data:
            data['ha_co2'] = co2_data.get("state", "Unknown")
            data['ha_co2_unit'] = co2_data.get("attributes", {}).get("unit_of_measurement", "ppm")
        else:
            data['ha_co2'] = "Error"
            data['ha_co2_unit'] = ""
        
        # IAQ from office sensor
        iaq_data = fetchHomeAssistantData("sensor.office_iaq_level")
        if iaq_data:
            data['ha_iaq'] = iaq_data.get("state", "Unknown")
        else:
            data['ha_iaq'] = "Error"
        
        # Grow lamp state
        lamp_data = fetchHomeAssistantData("light.grow_lamp")
        if lamp_data:
            data['grow_lamp_state'] = lamp_data.get("state", "Unknown")
        else:
            data['grow_lamp_state'] = "Unknown"
            
    except Exception as e:
        print(f"Error in aggregateData: {e}")
def addZero(x):
    if int(x) < 10:
        return '0' + str(x)
    return str(x)
def isButtonPressed(x, y):
    return (x >= button_x - button_width//2 and 
            x <= button_x + button_width//2 and 
            y >= button_y - button_height//2 and 
            y <= button_y + button_height//2)
def render():
    # Clear screen
    M5.Lcd.fillScreen(WHITE)
    M5.Lcd.setTextColor(BLACK, WHITE)
    
    # Header
    M5.Lcd.setTextSize(2)
    M5.Lcd.setCursor(12, 10)
    M5.Lcd.print(screenTitle)
    M5.Lcd.drawLine(0, 40, 540, 40, BLACK)
    
    # Current time in header
    t = data['currentTime']
    time_str = f"{addZero(t[3])}:{addZero(t[4])}"
    date_str = f"{t[0]}-{addZero(t[1])}-{addZero(t[2])}"
    M5.Lcd.setCursor(300, 10)
    M5.Lcd.print(f"{date_str}")
    
    # Main content area
    margin = 20
    y_pos = 80
    
    # Temperature display
    M5.Lcd.setCursor(margin, y_pos)
    M5.Lcd.print("Temperature:")
    
    # Add indicator based on value
    try:
        temp_value = float(data['ha_temperature'])
        indicator = ""
        if temp_value > 25:
            indicator = "!"  # High
        elif temp_value < 18:
            indicator = "!"  # Low
        else:
            indicator = ""   # Good
    except ValueError:
        indicator = "?"
    
    M5.Lcd.setTextSize(3)
    M5.Lcd.setCursor(250, y_pos)
    M5.Lcd.print(f"{data['ha_temperature']}{data['ha_temperature_unit']} {indicator}")
    y_pos += 80
    
    # Reset text size
    M5.Lcd.setTextSize(2)
    
    # Humidity display
    M5.Lcd.setCursor(margin, y_pos)
    M5.Lcd.print("Humidity:")
    
    try:
        humidity_value = float(data['ha_humidity'])
        indicator = ""
        if humidity_value > 60:
            indicator = "!"  # High
        elif humidity_value < 40:
            indicator = "!"  # Low
        else:
            indicator = ""   # Good
    except ValueError:
        indicator = "?"
    
    M5.Lcd.setTextSize(3)
    M5.Lcd.setCursor(250, y_pos)
    M5.Lcd.print(f"{data['ha_humidity']}{data['ha_humidity_unit']} {indicator}")
    y_pos += 80
    
    # Reset text size
    M5.Lcd.setTextSize(2)
    
    # CO2 display
    M5.Lcd.setCursor(margin, y_pos)
    M5.Lcd.print("CO2:")
    
    try:
        co2_value = float(data['ha_co2'])
        indicator = ""
        if co2_value > 1000:
            indicator = "!"  # Warning
        else:
            indicator = ""   # Good
    except ValueError:
        indicator = "?"
    
    M5.Lcd.setTextSize(3)
    M5.Lcd.setCursor(250, y_pos)
    M5.Lcd.print(f"{data['ha_co2']} {data['ha_co2_unit']} {indicator}")
    y_pos += 80
    
    # Reset text size
    M5.Lcd.setTextSize(2)
    
    # IAQ display
    M5.Lcd.setCursor(margin, y_pos)
    M5.Lcd.print("IAQ Level:")
    M5.Lcd.setTextSize(3)
    M5.Lcd.setCursor(250, y_pos)
    M5.Lcd.print(f"{data['ha_iaq']}")
    y_pos += 100
    
    # Reset text size
    M5.Lcd.setTextSize(2)
    
    # Divider line
    M5.Lcd.drawLine(margin, y_pos, 540-margin, y_pos, BLACK)
    y_pos += 40
    
    # Grow Lamp Control Button
    global button_y
    button_y = y_pos  # Set the button Y position dynamically
    
    # Draw button
    button_color = BLACK if data['grow_lamp_state'] == "on" else WHITE
    text_color = WHITE if data['grow_lamp_state'] == "on" else BLACK
    
    M5.Lcd.fillRoundRect(
        button_x - button_width//2, 
        button_y - button_height//2, 
        button_width, 
        button_height, 
        10,  # Corner radius
        button_color
    )
    
    # Draw button border
    M5.Lcd.drawRoundRect(
        button_x - button_width//2,
        button_y - button_height//2,
        button_width,
        button_height,
        10,
        BLACK
    )
    
    # Button text
    M5.Lcd.setTextSize(2)
    M5.Lcd.setTextColor(text_color, button_color)
    
    # Calculate centered text position
    text = "Grow Lamp: " + ("ON" if data['grow_lamp_state'] == "on" else "OFF")
    
    # Position for centered text
    text_width = len(text) * 12  # Approximate width based on font size
    text_x = button_x - text_width//2
    text_y = button_y - 10  # Adjust for vertical centering
    
    M5.Lcd.setCursor(text_x, text_y)
    M5.Lcd.print(text)
    
    # Reset text color
    M5.Lcd.setTextColor(BLACK, WHITE)
    
    y_pos += 80  # Add space below button
    
    # Divider line after button
    M5.Lcd.drawLine(margin, y_pos, 540-margin, y_pos, BLACK)
    y_pos += 40
    
    # Device information
    M5.Lcd.setCursor(margin, y_pos)
    M5.Lcd.print("Device Info:")
    y_pos += 40
    
    # Use smaller font for device info
    M5.Lcd.setTextSize(1)
    
    # Battery
    M5.Lcd.setCursor(margin, y_pos)
    M5.Lcd.print(f"Battery: {data['batteryPercentage']}")
    y_pos += 30
    
    # Voltage
    M5.Lcd.setCursor(margin, y_pos)
    M5.Lcd.print(f"Voltage: {data['voltage']}")
    y_pos += 30
    
    # WiFi status
    wifi_status = "Connected" if data['isWifiConnected'] else "Disconnected"
    M5.Lcd.setCursor(margin, y_pos)
    M5.Lcd.print(f"WiFi: {wifi_status}")
    y_pos += 30
    
    # Update interval
    M5.Lcd.setCursor(margin, y_pos)
    M5.Lcd.print(f"Update Interval: {int(wakeup_interval/60)} min")
    y_pos += 30
    
    # Last update time
    t = data['currentTime']
    update_time = f"{addZero(t[3])}:{addZero(t[4])}:{addZero(t[5])}"
    M5.Lcd.setCursor(margin, y_pos)
    M5.Lcd.print(f"Last Updated: {update_time}")
    
    # For e-ink displays
    try:
        M5.update()
    except:
        pass
# Main execution
try:
    # Initialize
    M5.begin()
    
    # Show loading message
    M5.Lcd.fillScreen(WHITE)
    M5.Lcd.setTextColor(BLACK, WHITE)
    M5.Lcd.setTextSize(2)
    M5.Lcd.setCursor(200, 240)
    M5.Lcd.print("Loading...")
    
    # Try to update display
    try:
        M5.update()
    except:
        pass
    
    # Fetch data and render display
    aggregateData()
    render()
    
    # Keep awake longer to allow interaction
    awake_time = 30  # 30 seconds instead of 10
    awake_start = time.time()
    last_touch_time = 0
    
    # Wait for touch events or timeout
    while time.time() - awake_start < awake_time:
        # Check for touches using native GT911 method
        if hasattr(M5, 'TP'):
            # For newer firmware with direct touch access
            if hasattr(M5.TP, 'getStatus') and M5.TP.getStatus():
                # Get touch point
                x = M5.TP.getX()
                y = M5.TP.getY()
                
                # Only process touch if it's a new touch (debounce)
                current_time = time.time()
                if current_time - last_touch_time > 0.5:
                    if isButtonPressed(x, y):
                        print(f"Button pressed at {x},{y}")
                        toggleGrowLamp()
                        # Reset timeout counter to give more time after interaction
                        awake_start = time.time()
                        last_touch_time = current_time
        
        # Alternative method for M5Paper with different touch API
        try:
            if hasattr(M5, 'TP') and hasattr(M5.TP, 'touched'):
                touches = M5.TP.touched()
                if touches > 0:
                    # Process first touch point
                    point = M5.TP.getPoint(0)
                    x, y = point.x, point.y
                    
                    # Only process touch if it's a new touch (debounce)
                    current_time = time.time()
                    if current_time - last_touch_time > 0.5:
                        if isButtonPressed(x, y):
                            print(f"Button pressed at {x},{y}")
                            toggleGrowLamp()
                            # Reset timeout counter to give more time after interaction
                            awake_start = time.time()
                            last_touch_time = current_time
        except:
            pass
            
        # Update display if needed
        try:
            M5.update()
        except:
            pass
            
        time.sleep(0.1)
    
    # Try to sleep
    try:
        M5.Power.deepSleep(wakeup_interval * 1000)
    except:
        try:
            import machine
            machine.deepsleep(wakeup_interval * 1000)
        except:
            while True:
                time.sleep(3600)  # Just sleep in a loop if deep sleep fails
    
except Exception as e:
    # Display error
    M5.Lcd.fillScreen(WHITE)
    M5.Lcd.setTextColor(BLACK, WHITE)
    M5.Lcd.setTextSize(2)
    M5.Lcd.setCursor(20, 200)
    M5.Lcd.print(f"Error: {str(e)}")
    
    try:
        M5.update()
    except:
        pass
    
    time.sleep(60)