Jump to content
Florian

Script to set openTime and closeTime to sunrise and sunset times.

Recommended Posts

This python script calculates the sunrise and sunset times for given coordinates and updates the configuration of an Omlet Smart Automatic Chicken Coop Door.
The script is intended to be executed at least once daily to update the opening and closing times for the next open and close events, depending on the current time.
It can be used with Docker and scheduled to run regularly using a cron job, either by starting the container or by running the script directly.

Environment-Variables needed:

TIMEZONE="..."
COOP_LATITUDE=
COOP_LONGITUDE=
OMLET_API_TOKEN="..."
OMLET_DEVICE_ID="..."

import os
from datetime import date, datetime, timedelta
from dateutil import tz
from suntime import Sun
from smartcoop.client import SmartCoopClient
from smartcoop.api.omlet import Omlet

LATITUDE = float(os.environ.get('COOP_LATITUDE'))
LONGITUDE = float(os.environ.get('COOP_LONGITUDE'))
TIMEZONE = tz.gettz(os.environ.get('TIMEZONE'))
OMLET_TOKEN = os.environ.get('OMLET_API_TOKEN')
DEVICE_ID = os.environ.get('OMLET_DEVICE_ID')

sun = Sun(LATITUDE, LONGITUDE)
today = datetime.today()
tomorrow = today + timedelta(days=1)
now = datetime.now(TIMEZONE)

sunrise_today_utc = sun.get_sunrise_time(today)
sunset_today_utc = sun.get_sunset_time(today)
sunrise_tomorrow_utc = sun.get_sunrise_time(tomorrow)
sunset_tomorrow_utc = sun.get_sunset_time(tomorrow)

sunrise_today = sunrise_today_utc.astimezone(TIMEZONE)
sunset_today = sunset_today_utc.astimezone(TIMEZONE)
sunrise_tomorrow = sunrise_tomorrow_utc.astimezone(TIMEZONE)
sunset_tomorrow = sunset_tomorrow_utc.astimezone(TIMEZONE)

print('Sunrise today:', sunrise_today)
print('Sunset today:', sunset_today)
print('Sunrise tomorrow:', sunrise_tomorrow)
print('Sunset tomorrow:', sunset_tomorrow)

if now > sunrise_today:
    open_time = sunrise_tomorrow
else:
    open_time = sunrise_today

if now > sunset_today:
    close_time = sunset_tomorrow
else:
    close_time = sunset_today

print('Open time:', open_time)
print('Close time:', close_time)

client = SmartCoopClient(client_secret=OMLET_TOKEN)
omlet = Omlet(client)
device = omlet.get_device_by_id(DEVICE_ID)

configuration = device.configuration

configuration.door.openMode = 'time'
configuration.door.openTime = open_time.strftime('%H:%M')
configuration.door.closeMode = 'time'
configuration.door.closeTime = close_time.strftime('%H:%M')

print('Configuration:', configuration)

omlet.update_configuration(device.deviceId, configuration)

print('Configuration updated')

 

Edited by Florian
Link to comment
Share on other sites

Posted (edited)

Added some limits (Environment-Variables😞

EARLIEST_OPEN_TIME="08:00"
LATEST_OPEN_TIME="10:00"
EARLIEST_CLOSE_TIME="18:00"
LATEST_CLOSE_TIME="20:00"

(edit: The function name p*****_optional_time is actually p a r s e_optional_time (without whitespaces) in the original, but it seems to be censored because "p a r s e" contains the word "a r s e".)

import os
import logging
from logging.handlers import RotatingFileHandler
from dotenv import load_dotenv
from datetime import datetime, timedelta
from dateutil import tz
from suntime import Sun
from smartcoop.client import SmartCoopClient
from smartcoop.api.omlet import Omlet

logger = logging.getLogger("smartcoop")
logger.setLevel(logging.INFO)

handler = RotatingFileHandler("log/smartcoop.log", maxBytes=1_000_000, backupCount=3)
formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
handler.setFormatter(formatter)

logger.addHandler(handler)
logger.addHandler(logging.StreamHandler())

load_dotenv()

def p*****_optional_time(value):
    """
    P***** HH:MM into datetime.time or return None.
    """
    if not value:
        return None

    return datetime.strptime(value, "%H:%M").time()


def apply_min_time(dt, min_time):
    """
    Ensure datetime is not earlier than min_time.
    """
    if min_time is None:
        return dt

    minimum = dt.replace(
        hour=min_time.hour,
        minute=min_time.minute,
        second=0,
        microsecond=0
    )

    return max(dt, minimum)


def apply_max_time(dt, max_time):
    """
    Ensure datetime is not later than max_time.
    """
    if max_time is None:
        return dt

    maximum = dt.replace(
        hour=max_time.hour,
        minute=max_time.minute,
        second=0,
        microsecond=0
    )

    return min(dt, maximum)

try:
    LATITUDE = float(os.environ.get("COOP_LATITUDE"))
    LONGITUDE = float(os.environ.get("COOP_LONGITUDE"))
    TIMEZONE = tz.gettz(os.environ.get("TIMEZONE"))
    OMLET_TOKEN = os.environ.get("OMLET_API_TOKEN")
    DEVICE_ID = os.environ.get("OMLET_DEVICE_ID")

    EARLIEST_OPEN_TIME = p*****_optional_time(
        os.environ.get("EARLIEST_OPEN_TIME", "08:00")
    )

    LATEST_OPEN_TIME = p*****_optional_time(
        os.environ.get("LATEST_OPEN_TIME", "10:00")
    )

    EARLIEST_CLOSE_TIME = p*****_optional_time(
        os.environ.get("EARLIEST_CLOSE_TIME", "18:00")
    )

    LATEST_CLOSE_TIME = p*****_optional_time(
        os.environ.get("LATEST_CLOSE_TIME", "20:00")
    )

    if None in [LATITUDE, LONGITUDE, TIMEZONE, OMLET_TOKEN, DEVICE_ID]:
        raise ValueError("Missing required environment variables")

except Exception as e:
    logger.critical(f"Configuration error: {e}", exc_info=True)
    raise SystemExit(1)

try:
    sun = Sun(LATITUDE, LONGITUDE)

    today = datetime.today()
    tomorrow = today + timedelta(days=1)
    now = datetime.now(TIMEZONE)

    sunrise_today = sun.get_sunrise_time(today).astimezone(TIMEZONE)
    sunset_today = sun.get_sunset_time(today).astimezone(TIMEZONE)
    sunrise_tomorrow = sun.get_sunrise_time(tomorrow).astimezone(TIMEZONE)
    sunset_tomorrow = sun.get_sunset_time(tomorrow).astimezone(TIMEZONE)

    logger.info(f"Sunrise today: {sunrise_today}")
    logger.info(f"Sunset today: {sunset_today}")
    logger.info(f"Sunrise tomorrow: {sunrise_tomorrow}")
    logger.info(f"Sunset tomorrow: {sunset_tomorrow}")

except Exception as e:
    logger.error(f"Failed to calculate sun times: {e}", exc_info=True)
    raise SystemExit(1)

try:
    open_time = sunrise_tomorrow if now > sunrise_today else sunrise_today
    close_time = sunset_tomorrow if now > sunset_today else sunset_today

    open_time = apply_min_time(open_time, EARLIEST_OPEN_TIME)
    open_time = apply_max_time(open_time, LATEST_OPEN_TIME)
    close_time = apply_min_time(close_time, EARLIEST_CLOSE_TIME)
    close_time = apply_max_time(close_time, LATEST_CLOSE_TIME)

    logger.info(f"Open time: {open_time}")
    logger.info(f"Close time: {close_time}")

except Exception as e:
    logger.error(f"Failed to determine open/close times: {e}", exc_info=True)
    raise SystemExit(1)

try:
    client = SmartCoopClient(client_secret=OMLET_TOKEN)
    omlet = Omlet(client)

    device = omlet.get_device_by_id(DEVICE_ID)
    configuration = device.configuration

    configuration.door.openMode = "time"
    configuration.door.openTime = open_time.strftime("%H:%M")
    configuration.door.closeMode = "time"
    configuration.door.closeTime = close_time.strftime("%H:%M")

    logger.info(f"New configuration: open={configuration.door.openTime}, close={configuration.door.closeTime}")

    omlet.update_configuration(device.deviceId, configuration)

    logger.info("Configuration updated successfully")

except Exception as e:
    logger.error(f"API error: {e}", exc_info=True)
    raise SystemExit(1)

 

Edited by Florian
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.




×
×
  • Create New...