class BME280:
    def __init__(self, i2c, addr=0x76):
        self.i2c = i2c
        self.addr = addr

        if addr not in i2c.scan():
            raise Exception("BME280 not found at address 0x{:02X}".format(addr))

        self._load_calibration()
        self._setup()

    def _read16(self, reg):
        return self.i2c.readfrom_mem(self.addr, reg, 2)

    def _readS16(self, reg):
        data = self._read16(reg)
        val = data[0] | (data[1] << 8)
        if val > 32767:
            val -= 65536
        return val

    def _readU16(self, reg):
        data = self._read16(reg)
        return data[0] | (data[1] << 8)

    def _load_calibration(self):
        cal = self.i2c.readfrom_mem(self.addr, 0x88, 26)

        self.dig_T1 = cal[0] | (cal[1] << 8)
        self.dig_T2 = self._to_signed(cal[2] | (cal[3] << 8))
        self.dig_T3 = self._to_signed(cal[4] | (cal[5] << 8))

        self.dig_H1 = self.i2c.readfrom_mem(self.addr, 0xA1, 1)[0]

        cal2 = self.i2c.readfrom_mem(self.addr, 0xE1, 7)
        self.dig_H2 = self._to_signed(cal2[0] | (cal2[1] << 8))
        self.dig_H3 = cal2[2]
        self.dig_H4 = self._to_signed((cal2[3] << 4) | (cal2[4] & 0x0F))
        self.dig_H5 = self._to_signed((cal2[5] << 4) | (cal2[4] >> 4))
        self.dig_H6 = self._to_signed(cal2[6])

    def _setup(self):
        self.i2c.writeto_mem(self.addr, 0xF2, b'\x01')
        self.i2c.writeto_mem(self.addr, 0xF4, b'\x27')

    def _to_signed(self, n):
        return n - 65536 if n > 32767 else n

    def read_raw(self):
        data = self.i2c.readfrom_mem(self.addr, 0xF7, 8)

        press = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4)
        temp  = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4)
        hum   = (data[6] << 8) | data[7]

        return temp, press, hum

    def read_compensated(self):
        raw_temp, _, raw_hum = self.read_raw()

        var1 = (((raw_temp >> 3) - (self.dig_T1 << 1)) * self.dig_T2) >> 11
        var2 = (((((raw_temp >> 4) - self.dig_T1) *
                 ((raw_temp >> 4) - self.dig_T1)) >> 12) *
                 self.dig_T3) >> 14

        t_fine = var1 + var2
        temp = (t_fine * 5 + 128) >> 8
        temperature = temp / 100

        v_x1 = t_fine - 76800
        v_x1 = (((((raw_hum << 14) - (self.dig_H4 << 20) -
                  (self.dig_H5 * v_x1)) + 16384) >> 15) *
                (((((((v_x1 * self.dig_H6) >> 10) *
                (((v_x1 * self.dig_H3) >> 11) + 32768)) >> 10)
                + 2097152) * self.dig_H2 + 8192) >> 14))

        v_x1 = v_x1 - (((((v_x1 >> 15) * (v_x1 >> 15)) >> 7)
                        * self.dig_H1) >> 4)

        v_x1 = max(0, min(v_x1, 419430400))
        humidity = (v_x1 >> 12) / 1024.0

        return temperature, humidity