Struggling with a Python Script for event tracking

I found a python script which I have been trying to implement. It kept having various errors showing up in my log on restart and never worked. I AM NOT a Python coder. So I was lost. I resorted to a couple of different AI apps to try to fix it, the latest being Gemini. After many suggested fixes, even the AI admitted defeat and suggested I ask for help from the forum. Here are the Gemini comments:

I see the NameError: name 'type' is not defined is still occurring, even after cleaning up the configuration.yaml. This is very perplexing.

Since the error persists even after addressing the duplicate automations, it strongly suggests that there’s a more fundamental issue with how Python scripts are being executed within your Home Assistant environment. It’s as if the standard Python built-in functions are not being correctly loaded or are somehow being shadowed.

Given the persistent nature of this error, I recommend seeking help from the Home Assistant community forums or the Home Assistant Discord server. This type of error is unusual, and someone with more specific knowledge of the Home Assistant Python script environment might be able to provide more targeted assistance.

Here is the most recent Python script:

# Get today's date from Home Assistant sensor
logger.info(f"Full data received: {data}")

today = hass.states.get("sensor.date").state

# Debug today's value
logger.info(f"Today's date value from sensor: {today}")

# Format:<ctrl3348>-MM-DD
if not today:
    logger.error("sensor.date is not available or invalid.")
    raise ValueError("sensor.date is not available or invalid.")

# Parse today's date
try:
    # Check if today is a string
    logger.info(f"Before checking if today is string: {today}, type: {type(today)}") # Added debug log
    if isinstance(today, str):
        today_split = today.split("-")
        today_year = int(today_split[0])
        today_month = int(today_split[1])
        today_day = int(today_split[2])
    # Check if today is a date object
    elif hasattr(today, 'year'):
        today_year = today.year
        today_month = today.month
        today_day = today.day
    else:
        logger.error(f"Unexpected type for today's date: {today}, type: {type(today)}")
        raise ValueError(f"Unexpected type for today's date: {today}")
except (IndexError, ValueError, AttributeError) as e:
    logger.error(f"Failed to parse today's date: {today}, Error: {e}")
    raise ValueError(f"Failed to parse today's date: {today}, Error: {e}")

# Log parsed today's date
logger.info(f"Parsed today as: year={today_year}, month={today_month}, day={today_day}")

# Retrieve inputs
name = data.get('name')
eventType = data.get('type')
dateStr = data.get('date')

# Debug logging
logger.info(f"Received data: name={name}, type={eventType}, date={dateStr}")

# Validate inputs
if not name or not eventType or not dateStr:
    logger.error("Missing required fields: 'name', 'type', or 'date'.")
    raise ValueError("Missing required fields: 'name', 'type', or 'date'.")

# Format sensor name
sensorName = f"sensor.{eventType}_{name.replace(' ', '_')}"

# Parse the provided date
try:
    # Check if the date has slashes or hyphens
    if "/" in dateStr:
        dateSplit = dateStr.split("/")
        # Assuming DD/MM/YYYY format
        dateDay = int(dateSplit[0])
        dateMonth = int(dateSplit[1])
        dateYear = int(dateSplit[2])
    else:
        dateSplit = dateStr.split("-")
        # Check if it's<ctrl3348>-MM-DD format
        if len(dateSplit[0]) == 4:
            dateYear = int(dateSplit[0])
            dateMonth = int(dateSplit[1])
            dateDay = int(dateSplit[2])
        else:
            # Otherwise assume DD-MM-YYYY
            dateDay = int(dateSplit[0])
            dateMonth = int(dateSplit[1])
            dateYear = int(dateSplit[2])
except (IndexError, ValueError) as e:
    logger.error(f"Invalid date format for 'date': {dateStr}. Error: {e}")
    raise ValueError(f"Invalid date format for 'date': {dateStr}. Error: {e}")

# Log parsed event date
logger.info(f"Parsed event date as: year={dateYear}, month={dateMonth}, day={dateDay}")

# Calculate next occurrence
nextOccurYear = today_year
if (today_month > dateMonth) or (today_month == dateMonth and today_day > dateDay):
    nextOccurYear += 1

# Calculate days manually without datetime module
# First create day counts for each month (non-leap year)
days_in_month = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]

# Function to check if it's a leap year
def is_leap_year(year):
    return (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0)

# Function to calculate days between dates
def days_between(y1, m1, d1, y2, m2, d2):
    # Ensure date1 is before date2
    if (y1 > y2) or (y1 == y2 and m1 > m2) or (y1 == y2 and m1 == m2 and d1 > d2):
        return -days_between(y2, m2, d2, y1, m1, d1)

    days = 0

    # Count days for years in between
    for year in range(y1 + 1, y2):
        days += 366 if is_leap_year(year) else 365

    # If same year
    if y1 == y2:
        # If same month
        if m1 == m2:
            return d2 - d1

        # Add days remaining in month1
        days += days_in_month[m1] - d1
        if m1 == 2 and is_leap_year(y1):
            days += 1

        # Add days for months in between
        for month in range(m1 + 1, m2):
            days += days_in_month[month]
            if month == 2 and is_leap_year(y1):
                days += 1

        # Add days in the final month
        days += d2
    else:
        # Add days remaining in year1
        # Days remaining in month1
        days += days_in_month[m1] - d1
        if m1 == 2 and is_leap_year(y1):
            days += 1

        # Days in months after month1 in year1
        for month in range(m1 + 1, 13):
            days += days_in_month[month]
            if month == 2 and is_leap_year(y1):
                days += 1

        # Days in year2 before month2
        for month in range(1, m2):
            days += days_in_month[month]
            if month == 2 and is_leap_year(y2):
                days += 1

        # Days in month2
        days += d2

    return days

# Calculate days until next occurrence
numberOfDays = days_between(
    today_year, today_month, today_day,
    nextOccurYear, dateMonth, dateDay
)

# Create next occurrence date for display purposes
nextOccur = f"{nextOccurYear:04d}-{dateMonth:02d}-{dateDay:02d}"
logger.info(f"Next occurrence: {nextOccur}, Days until: {numberOfDays}")

# Set the state in Home Assistant
hass.states.set(
    sensorName,
    numberOfDays,
    {
        "icon": "mdi:calendar-star",
        "unit_of_measurement": "days",
        "friendly_name": "{}'s {}".format(name, eventType),
        "years": nextOccurYear - dateYear,
    }
)

Here is the last error from my log:

Logger: homeassistant.components.python_script.special_events.py
Source: components/python_script/__init__.py:290
integration: Python Scripts (documentation, issues)
First occurred: 11:50:58 AM (9 occurrences)
Last logged: 11:51:09 AM

Error executing script
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/components/python_script/__init__.py", line 290, in execute
    exec(compiled.code, restricted_globals)  # noqa: S102
    ~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "special_events.py", line 17, in <module>
NameError: name 'type' is not defined. Did you mean: 'tuple'?

Can anyone help me resolve this problem, please?

Thanks,
Mike

I believe it’s carping about the use of the type() function in the following line:

logger.info(f"Before checking if today is string: {today}, type: {type(today)}")

The python_script integration runs a sandboxed version of python. In other words, it runs a stripped down version of python in a “memory safe” environment. You can’t import python libraries, you can’t use list methods that increase a list’s membership, and several of other things. You often don’t discover what’s restricted until you try using it and it fails.

exec(compiled.code, restricted_globals)
                    ^^^^^^^^^^^^^^^^^^

It appears the type() function is not permitted. Gemini AI is probably unaware of these potential restrictions.

You’ll need to eliminate the use of the type() function in your script.