Source code for adafruit_featherwing.rtc_featherwing

# SPDX-FileCopyrightText: 2019 Melissa LeBlanc-Williams for Adafruit Industries
#
# SPDX-License-Identifier: MIT

"""
`adafruit_featherwing.rtc_featherwing`
====================================================

Helper for using the `DS3231 Precision RTC FeatherWing
<https://www.adafruit.com/product/3028>`_.

* Author(s): Melissa LeBlanc-Williams
"""

__version__ = "0.0.0+auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_FeatherWing.git"

import time
from collections import namedtuple
import board
import adafruit_ds3231

try:
    from typing import Optional, Dict
    from busio import I2C
except ImportError:
    pass


[docs] class RTCFeatherWing: """Class representing an `DS3231 Precision RTC FeatherWing <https://www.adafruit.com/product/3028>`_. Automatically uses the feather's I2C bus.""" def __init__(self, i2c: Optional[I2C] = None): if i2c is None: i2c = board.I2C() self._rtc = adafruit_ds3231.DS3231(i2c) def __setitem__(self, index: str, value: int): """ Allow updates using setitem if that makes it easier """ self._set_time_value(index, value) def __getitem__(self, index: str): """ Allow retrievals using getitem if that makes it easier """ return self._get_time_value(index) def _set_time_value(self, unit: str, value: int): """ Set just the specific unit of time """ now = self._get_now() if unit in now: now[unit] = value else: raise ValueError("The specified unit of time is invalid") self._rtc.datetime = self._encode(now) def _get_time_value(self, unit: str): """ Get just the specific unit of time """ now = self._get_now() if unit in now: return now[unit] raise ValueError("The specified unit of time is invalid") def _get_now(self) -> Dict[str, int]: """ Return the current date and time in a nice updatable dictionary """ now = self._rtc.datetime return { "second": now.tm_sec, "minute": now.tm_min, "hour": now.tm_hour, "day": now.tm_mday, "month": now.tm_mon, "year": now.tm_year, "weekday": now.tm_wday, } def _encode(self, date: Dict[str, int]) -> Dict[str, int]: """ Encode the updatable dictionary back into a time struct """ now = self._rtc.datetime return time.struct_time( ( date["year"], date["month"], date["day"], date["hour"], date["minute"], date["second"], date["weekday"], now.tm_yday, now.tm_isdst, ) )
[docs] def is_leap_year(self, year: Optional[int] = None) -> bool: """ Check if the year is a leap year :param int year: (Optional) The year to check. If none is provided, current year is used. """ if year is None: year = self._get_time_value("year") return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
[docs] def get_month_days( self, month: Optional[int] = None, year: Optional[int] = None ) -> int: """ Return the number of days for the month of the given year :param int month: (Optional) The month to use. If none is provided, current month is used. :param int year: (Optional) The year to check. If none is provided, current year is used. """ if month is None: month = self._get_time_value("month") leap_year = self.is_leap_year(year) max_days = (31, 29 if leap_year else 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31) return max_days[month - 1]
[docs] def set_time(self, hour: int, minute: int, second: int = 0): """ Set the time only :param int hour: The hour we want to set the time to :param int minute: The minute we want to set the time to :param int second: (Optional) The second we want to set the time to (default=0) """ if not isinstance(second, int) or not 0 <= second < 60: raise ValueError("The second must be an integer in the range of 0-59") if not isinstance(minute, int) or not 0 <= minute < 60: raise ValueError("The minute must be an integer in the range of 0-59") if not isinstance(hour, int) or not 0 <= hour < 24: raise ValueError("The hour must be an integer in the range of 0-23") now = self._get_now() now["hour"] = hour now["minute"] = minute now["second"] = second self._rtc.datetime = self._encode(now)
[docs] def set_date(self, day: int, month: int, year: int): """ Set the date only :param int day: The day we want to set the date to :param int month: The month we want to set the date to :param int year: The year we want to set the date to """ if not isinstance(year, int): raise ValueError("The year must be an integer") if not isinstance(month, int) or not 1 <= month <= 12: raise ValueError("The month must be an integer in the range of 1-12") month_days = self.get_month_days(month, year) if not isinstance(day, int) or not 1 <= day <= month_days: raise ValueError( "The day must be an integer in the range of 1-{}".format(month_days) ) now = self._get_now() now["day"] = day now["month"] = month now["year"] = year self._rtc.datetime = self._encode(now)
@property def datetime(self): """ Passthru property to the ds3231 library for compatibility """ return self._rtc.datetime @datetime.setter def datetime(self, datetime: time.struct_time): self._rtc.datetime = datetime @property def year(self): """ The Current Year """ return self._get_time_value("year") @year.setter def year(self, year: int): if isinstance(year, int): self._set_time_value("year", year) else: raise ValueError("The year must be an integer") @property def month(self): """ The Current Month """ return self._get_time_value("month") @month.setter def month(self, month: int): if isinstance(month, int) and 1 <= month <= 12: self._set_time_value("month", month) else: raise ValueError("The month must be an integer in the range of 1-12") @property def day(self): """ The Current Day """ return self._get_time_value("day") @day.setter def day(self, day: int): month_days = self.get_month_days() if isinstance(day, int) and 1 <= day <= month_days: self._set_time_value("day", day) else: raise ValueError( "The day must be an integer in the range of 1-{}".format(month_days) ) @property def hour(self): """ The Current Hour """ return self._get_time_value("hour") @hour.setter def hour(self, hour: int): if isinstance(hour, int) and 0 <= hour < 24: self._set_time_value("hour", hour) else: raise ValueError("The hour must be an integer in the range of 0-23") @property def minute(self): """ The Current Minute """ return self._get_time_value("minute") @minute.setter def minute(self, minute: int): if isinstance(minute, int) and 0 <= minute < 60: self._set_time_value("minute", minute) else: raise ValueError("The minute must be an integer in the range of 0-59") @property def second(self): """ The Current Second """ return self._get_time_value("second") @second.setter def second(self, second: int): if isinstance(second, int) and 0 <= second < 60: self._set_time_value("second", second) else: raise ValueError("The second must be an integer in the range of 0-59") @property def weekday(self): """ The Current Day of the Week Value (0-6) where Sunday is 0 """ return self._get_time_value("weekday") @weekday.setter def weekday(self, weekday: int): if isinstance(weekday, int) and 0 <= weekday < 7: self._set_time_value("weekday", weekday) else: raise ValueError("The weekday must be an integer in the range of 0-6") @property def now(self): """ The Current Date and Time in Named Tuple Style (Read Only) """ date_time = namedtuple("DateTime", "second minute hour day month year weekday") return date_time(**self._get_now()) @property def unixtime(self): """ The Current Date and Time in Unix Time """ try: return time.mktime(self._rtc.datetime) except (AttributeError, RuntimeError) as error: print("Error attempting to run time.mktime() on this board\n", error) return None @unixtime.setter def unixtime(self, unixtime: int): if isinstance(unixtime, int): try: self._rtc.datetime = time.localtime(unixtime) except (AttributeError, RuntimeError) as error: print("Error attempting to run time.localtime() on this board\n", error)