From a0696e5917c6fe4a562e7d64909c5868101371b6 Mon Sep 17 00:00:00 2001 From: Mickael Date: Wed, 10 Nov 2021 17:15:20 +0100 Subject: [PATCH] add properties and setters, better instance management and better testing --- GreenPonik_Thermistor10k/Thermistor10k.py | 157 +++++++++++----------- docs/source/conf.py | 6 +- docs/source/index.rst | 2 +- requirements.txt | 2 +- tests/test_Thermistor10k.py | 91 ++++++++++++- 5 files changed, 170 insertions(+), 88 deletions(-) diff --git a/GreenPonik_Thermistor10k/Thermistor10k.py b/GreenPonik_Thermistor10k/Thermistor10k.py index edd7efd..edd4915 100644 --- a/GreenPonik_Thermistor10k/Thermistor10k.py +++ b/GreenPonik_Thermistor10k/Thermistor10k.py @@ -29,10 +29,29 @@ class Thermistor10k: DEFAULT_BUS = 1 THERMISTOR_OFFSET = 2000 + """The ADS1015 and ADS1115 both have the same gain options. + GAIN RANGE (V) + ---- --------- + 2/3 +/- 6.144 + 1 +/- 4.096 + 2 +/- 2.048 + 4 +/- 1.024 + 8 +/- 0.512 + 16 +/- 0.256 + """ + def __init__(self, bus=DEFAULT_BUS, addr=DEFAULT_ADDR): self._bus = bus self._addr = addr self._debug = False + self._gains = [g for g in _ADS1X15_CONFIG_GAIN.keys()] + self._gain = self._gains[1] # defautl gain to 1: 512 => 4.096V + self._i2c = I2C(self._bus) + self._ads = ADS_1115.ADS1115( + i2c=self._i2c, gain=self._gain, address=self._addr + ) # Create the ADS object # ads = ADS_1015.ADS1015() + self._ads_chan_selector = ADS_1115.P2 + self._ads_chan = AnalogIn(self._ads, self._ads_chan_selector) # AnalogIn(ads, ADS_1015.P2) def __enter__(self): """Context manager enter function.""" @@ -44,14 +63,56 @@ def __exit__(self, exc_type, exc_val, exc_tb): """Context manager exit function, ensures resources are cleaned up.""" return False # Don't suppress exceptions. + @property + def i2c(self): + return self._i2c + + @property + def ads_instance(self): + return self._ads + + @property + def ads_channel_selector(self): + return self._ads_chan_selector + + @ads_channel_selector.setter + def ads_channel_selector(self, c): + assert(c in [ADS_1115.P0, ADS_1115.P1, ADS_1115.P2, ADS_1115.P3]) + self._ads_chan_selector = c + + @property + def ads_channel_read(self): + return self._ads_chan + @property def bus(self): return self._bus + @bus.setter + def bus(self, b): + self._bus = b + @property def address(self): return self._addr + @address.setter + def address(self, a): + self._addr = a + + @property + def gains(self): + return self._gains + + @property + def gain(self): + return self._gain + + @gain.setter + def gain(self, g): + assert(g in self._gains) + self._gain = g + @property def debug(self): return self._debug @@ -60,24 +121,6 @@ def debug(self): def debug(self, d): self._debug = d - """ - # DEPRECATED #### - # def slope_calculator(self, x_list, y_list): - # - # slope m = DY/DX - # - # slope = (y_list[1] - y_list[0]) / (x_list[1] - x_list[0]) - # return slope - - # def intercept_calculator(self, slope, x, y): - # - # b = y -mx - # - # intercept = y - slope * x - # return intercept - # DEPRECATED #### - """ - def steinhart_temperature(self, r, ro=10000.0, to=25.0, beta=3950.0): """ @brief steinhart formula for thermistor resistance conversion to celcius degrees @@ -104,67 +147,21 @@ def read_temp(self): # init temp value to default error code (convension GreenPonik) temp = 9999.999 try: - with I2C(self._bus) as i2c: - """ - # DEPRECATED #### - # these points are determinated by lab test with both ec probe and manual thermometer - # # x1 = 12272 - # # y1 = 25.9 - # # x2 = 10752 - # # y2 = 34.5 - - # # SLOPE = self.slope_calculator([x1, x2], [y1, y2]) - # # INTERCEPT = self.intercept_calculator(SLOPE, x1, y1) - - # # if self._debug is True: - # # print("slope: ", SLOPE) - # # print("intercept: ", INTERCEPT) - # DEPRECATED #### - """ - - """The ADS1015 and ADS1115 both have the same gain options. - GAIN RANGE (V) - ---- --------- - 2/3 +/- 6.144 - 1 +/- 4.096 - 2 +/- 2.048 - 4 +/- 1.024 - 8 +/- 0.512 - 16 +/- 0.256 - """ - gains = [g for g in _ADS1X15_CONFIG_GAIN.keys()] - # Create the ADS object - # ads = ADS_1015.ADS1015( - ads = ADS_1115.ADS1115( - i2c, - gain=gains[0], - address=self._addr, - ) - # adc2 = AnalogIn(ads, ADS_1015.P2) - adc2 = AnalogIn(ads, ADS_1115.P2) - value = adc2.value - resistance = (10000 * value / (32767 - value)) - self.THERMISTOR_OFFSET - - temp = self.steinhart_temperature(resistance) - - # temp = (adc2.value * SLOPE) + INTERCEPT - if self._debug is True: - print("adc2 analog: ", adc2.value) - print("adc2 voltage: ", adc2.voltage) - print("thermistor resistance: ", resistance) - print("Thermistor 10k temperature: %s" % (temp)) - - """ - # DEPRECATED #### - # if adc2.value >= 17500 or adc2.voltage >= 3.25: - # return 9999.999 # return error code can allow user to know if thermistor doesn't connected - # else: - # return temp - # DEPRECATED #### - """ - if temp >= 100 or temp <= 0: - return 9999.999 - else: - return temp + adc_channel = self._ads_chan + value = adc_channel.value + resistance = (10000 * value / (32767 - value)) - self.THERMISTOR_OFFSET + + temp = self.steinhart_temperature(resistance) + + if self._debug is True: + print("adc2 analog: ", adc_channel.value) + print("adc2 voltage: ", adc_channel.voltage) + print("thermistor resistance: ", resistance) + print("Thermistor 10k temperature: %s" % (temp)) + + if temp >= 100 or temp <= 0: + return 9999.999 + else: + return temp except Exception as e: print("An exception occurred in read_temp(): {}".format(e)) diff --git a/docs/source/conf.py b/docs/source/conf.py index 73d844d..aa52d61 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -31,7 +31,7 @@ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'sphinx-press-theme', + # 'sphinx_press_theme', 'sphinx_rtd_theme', 'recommonmark' ] @@ -50,9 +50,9 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -# html_theme = 'alabaster' +html_theme = 'alabaster' # html_theme = 'sphinx_rtd_theme' -html_theme = 'press' +# html_theme = 'press' # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, diff --git a/docs/source/index.rst b/docs/source/index.rst index 0af3b6f..7b8b854 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -11,7 +11,7 @@ Welcome to GreenPonik Thermistor 10k's documentation! :caption: Contents: modules - + Index Indices and tables ================== diff --git a/requirements.txt b/requirements.txt index f9642ff..63c4120 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,5 +14,5 @@ flake8>=3.8.3 check-manifest>=0.42 Sphinx==4.2.0 sphinx-rtd-theme>=1.0.0 -sphinx-press-theme>=0.8.0 +sphinx_press_theme==0.8.0 recommonmark>=0.7.1 \ No newline at end of file diff --git a/tests/test_Thermistor10k.py b/tests/test_Thermistor10k.py index 4407b5e..1e2a294 100644 --- a/tests/test_Thermistor10k.py +++ b/tests/test_Thermistor10k.py @@ -5,24 +5,109 @@ import unittest import sys from unittest.mock import patch +# from functools import wraps + +# PRINT_ON = True + + +# def toggle_print(p): +# global PRINT_ON +# PRINT_ON = p + + +# def printf(f): +# @wraps(f) +# def wrapped(*args, **kwargs): +# r = f(*args, **kwargs) + +# if len(args): +# c = str(args[0].__class__).split("'")[1] # grab self from the class method +# else: +# c = "" # no class + +# if PRINT_ON: +# if r: +# print("{}.{}{}: {}".format(c, f.__name__, args[1:], r)) +# else: +# print("{}.{}{}".format(c, f.__name__, args[1:])) +# return r + +# return wrapped + + +# class Base(object): +# def __init__(self, name=None): +# print("<<< WARNING: using fake waterbrain >>>") +# if name: +# print("<<< Using: {} >>>".format(name)) class SmbusMock(): - # simultate the smbus method just for tests + # simultate the smbus class just for tests pass -class FCNTLMock: +class FcntlMock(): + # simultate the fcntl class just for tests def ioctl(self): # simultate the FCNTL.ioctl method just for tests pass -sys.modules["fcntl"] = FCNTLMock() +class AdafruitExtendedBusMock: + # simultate the adafruit_extended_bus class just for tests + def init(): + pass + + class ExtendedI2C(): + # simultate the ExtendedI2C class just for tests + # @printf + # def __init__(self, bus_id=1, frequency=0): + # Base.__init__(self, self.__class__) + # pass + + pass + + pass + + +sys.modules["fcntl"] = FcntlMock() sys.modules["smbus"] = SmbusMock() +sys.modules["adafruit_extended_bus"] = AdafruitExtendedBusMock() class TestThermistor10k(unittest.TestCase): + @patch("adafruit_extended_bus.ExtendedI2C") + def test_properies(self, mock_i2c): + from adafruit_ads1x15.ads1x15 import _ADS1X15_CONFIG_GAIN + + fixtures = { + "bus": 1, + "gain": [g for g in _ADS1X15_CONFIG_GAIN.keys()][1], + "address": 12, + "debug": True, + "ads_channel_selector": 2, + } + from GreenPonik_Thermistor10k.Thermistor10k import Thermistor10k + th = Thermistor10k() + + th.bus = fixtures["bus"] + th.gain = fixtures["gain"] + th.address = fixtures["address"] + th.debug = fixtures["debug"] + th.ads_channel_selector = fixtures["ads_channel_selector"] + + self.assertIsNotNone(th.bus) + self.assertIsNotNone(th.gain) + self.assertIsNotNone(th.address) + self.assertIsNotNone(th.debug) + self.assertIsNotNone(th.ads_channel_selector) + self.assertTrue(th.bus, fixtures["bus"]) + self.assertTrue(th.gain, fixtures["gain"]) + self.assertTrue(th.address, fixtures["address"]) + self.assertTrue(th.debug, fixtures["debug"]) + self.assertTrue(th.ads_channel_selector, fixtures["ads_channel_selector"]) + @patch("GreenPonik_Thermistor10k.Thermistor10k.Thermistor10k") def test_read_temp(self, mock_th): th = mock_th()