# SPDX-FileCopyrightText: 2017 Scott Shawcroft for Adafruit Industries
# SPDX-FileCopyrightText: 2021 Jose David Montoya
# SPDX-License-Identifier: MIT
"""
`adafruit_mcp9808`
====================================================
CircuitPython library to support MCP9808 high accuracy temperature sensor.
* Author(s): Scott Shawcroft, Jose David M.
Implementation Notes
--------------------
**Hardware:**
* `Adafruit MCP9808 High Accuracy I2C Temperature Sensor Breakout
  <https://www.adafruit.com/products/1782>`_ (Product ID: 1782)
**Software and Dependencies:**
* Adafruit CircuitPython firmware for the supported boards:
  https://github.com/adafruit/circuitpython/releases
* Adafruit's Bus Device library:
  https://github.com/adafruit/Adafruit_CircuitPython_BusDevice
* Adafruit's Register library:
  https://github.com/adafruit/Adafruit_CircuitPython_Register
**Notes:**
#.  Datasheet: http://www.adafruit.com/datasheets/MCP9808.pdf
"""
from micropython import const
from adafruit_bus_device.i2c_device import I2CDevice
from adafruit_register.i2c_bits import RWBits
from adafruit_register.i2c_bit import ROBit
__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_MCP9808.git"
_MCP9808_DEFAULT_ADDRESS = const(0x18)
_MCP9808_DEVICE_ID = const(0x54)
_MCP9808_REG_CONFIGURATION = const(0x01)
_MCP9808_REG_UPPER_TEMP = const(0x02)
_MCP9808_REG_LOWER_TEMP = const(0x03)
_MCP9808_REG_CRITICAL_TEMP = const(0x04)
_MCP9808_REG__TEMP = const(0x05)
_MCP9808_REG_MANUFACTURER_ID = const(0x06)
_MCP9808_REG_DEVICE_ID = const(0x07)
_MCP9808_REG_RESOLUTION = const(0x08)
# Resolution settings
_MCP9808_RESOLUTION_HALF_C = const(0x0)
_MCP9808_RESOLUTION_QUARTER_C = const(0x1)
_MCP9808_RESOLUTION_EIGHTH_C = const(0x2)
_MCP9808_RESOLUTION_SIXTEENTH_C = const(0x3)
[docs]class MCP9808:
    """Interface to the MCP9808 temperature sensor.
    :param ~busio.I2C i2c_bus: The I2C bus the MCP9808 is connected to.
    :param int address: The I2C address of the device. Defaults to :const:`0x18`
    **MCP9808 Settings**
        You could set the MCP9808 with different temperature limits and compare them with the
        ambient temperature Ta
        - above_ct this value will be set to `True` when Ta is above this limit
        - above_ut: this value will be set to `True` when Ta is above this limit
        - below_lt: this value will be set to `True` when Ta is below this limit
        To get this value, you will need to read the temperature, and then access the attribute
    **Quickstart: Importing and using the MCP9808**
        Here is an example of using the :class:`MCP9808` class.
        First you will need to import the libraries to use the sensor
        .. code-block:: python
            import board
            import adafruit_mcp9808
        Once this is done you can define your `board.I2C` object and define your sensor object
        .. code-block:: python
            i2c = board.I2C()   # uses board.SCL and board.SDA
            mcp = adafruit_mcp9808.MCP9808(i2c_bus)
        Now you have access to the change in temperature using the
        :attr:`temperature` attribute. This temperature is in Celsius.
        .. code-block:: python
            temperature = mcp.temperature
    """
    _MCP9808_REG_RESOLUTION_SET = RWBits(2, 0x08, 0, register_width=2)
    above_critical = ROBit(_MCP9808_REG__TEMP, 7, register_width=1)
    """True when the temperature is above the currently
    set critical temperature. False Otherwise"""
    above_upper = ROBit(_MCP9808_REG__TEMP, 6, register_width=1)
    """True when the temperature is above the currently
    set high temperature. False Otherwise"""
    below_lower = ROBit(_MCP9808_REG__TEMP, 5, register_width=1)
    """True when the temperature is below the currently
    set lower temperature. False Otherwise"""
    def __init__(self, i2c_bus, address=_MCP9808_DEFAULT_ADDRESS):
        self.i2c_device = I2CDevice(i2c_bus, address)
        # Verify the manufacturer and device ids to ensure we are talking to
        # what we expect.
        self.buf = bytearray(3)
        self.buf[0] = _MCP9808_REG_MANUFACTURER_ID
        with self.i2c_device as i2c:
            i2c.write_then_readinto(self.buf, self.buf, out_end=1, in_start=1)
        ok = self.buf[2] == _MCP9808_DEVICE_ID and self.buf[1] == 0
        # Check device id.
        self.buf[0] = _MCP9808_REG_DEVICE_ID
        with self.i2c_device as i2c:
            i2c.write_then_readinto(self.buf, self.buf, out_end=1, in_start=1)
        if not ok or self.buf[1] != 0x04:
            raise ValueError(
                "Unable to find MCP9808 at i2c address " + str(hex(address))
            )
    @property
    def temperature(self):
        """Temperature in Celsius. Read-only."""
        self.buf[0] = _MCP9808_REG__TEMP
        with self.i2c_device as i2c:
            i2c.write_then_readinto(self.buf, self.buf, out_end=1, in_start=1)
        return self._temp_conv()
    def _temp_conv(self):
        """Internal function to convert temperature given by the sensor"""
        # Clear flags from the value
        self.buf[1] = self.buf[1] & 0x1F
        if self.buf[1] & 0x10 == 0x10:
            self.buf[1] = self.buf[1] & 0x0F
            return (self.buf[1] * 16 + self.buf[2] / 16.0) - 256
        return self.buf[1] * 16 + self.buf[2] / 16.0
    def _limit_temperatures(self, temp, t_address=0x02):
        """Internal function to setup limit temperature
        :param int temp: temperature limit
        :param int t_address: register address for the temperature limit
                                0x02 : Upper Limit
                                0x03 : Lower Limit
                                0x04 : Critical Limit
        """
        if temp < 0:
            negative = True
            temp = abs(temp)
        else:
            negative = False
        self.buf[0] = t_address
        self.buf[1] = temp >> 4
        if negative:
            self.buf[1] = self.buf[1] | 0x10
        self.buf[2] = (temp & 0x0F) << 4
        with self.i2c_device as i2c:
            i2c.write(self.buf)
    def _get_temperature(self, address):
        self.buf[0] = address
        with self.i2c_device as i2c:
            i2c.write_then_readinto(self.buf, self.buf, out_end=1, in_start=1)
        return self._temp_conv()
    def _set_temperature(self, temp, address):
        self._limit_temperatures(temp, address)
    @property
    def upper_temperature(self):
        """Upper alarm temperature in Celsius"""
        return self._get_temperature(_MCP9808_REG_UPPER_TEMP)
    @upper_temperature.setter
    def upper_temperature(self, temp):
        """Setup Upper temperature"""
        self._limit_temperatures(temp, _MCP9808_REG_UPPER_TEMP)
    @property
    def lower_temperature(self):
        """Lower alarm temperature in Celsius"""
        return self._get_temperature(_MCP9808_REG_LOWER_TEMP)
    @lower_temperature.setter
    def lower_temperature(self, temp):
        """Setup Lower temperature"""
        self._limit_temperatures(temp, _MCP9808_REG_LOWER_TEMP)
    @property
    def critical_temperature(self):
        """Critical alarm temperature in Celsius"""
        return self._get_temperature(_MCP9808_REG_CRITICAL_TEMP)
    @critical_temperature.setter
    def critical_temperature(self, temp):
        """Setup Critical temperature"""
        self._limit_temperatures(temp, _MCP9808_REG_CRITICAL_TEMP)
    @property
    def resolution(self):
        """Temperature Resolution in Celsius
        =======   ============   ==============
         Value     Resolution     Reading Time
        =======   ============   ==============
          0          0.5°C            30 ms
          1          0.25°C           65 ms
          2         0.125°C          130 ms
          3         0.0625°C         250 ms
        =======   ============   ==============
        """
        return self._MCP9808_REG_RESOLUTION_SET
    @resolution.setter
    def resolution(self, resol_value=3):
        """ Setup Critical temperature"""
        self._MCP9808_REG_RESOLUTION_SET = resol_value  # pylint: disable=invalid-name