From: Philip Howard Date: Wed, 29 Apr 2020 11:08:59 +0000 (+0100) Subject: Merge pull request #57 from pimoroni/test-tweaks-and-linting X-Git-Url: https://zdv2.bktei.com/gitweb/EVA-2020-02-2.git/commitdiff_plain/87221e4160e484a01d53d66e59db4c57c2904495?hp=97ee1d88e84df79d82c3ca20eb89378c65197f9a Merge pull request #57 from pimoroni/test-tweaks-and-linting Test tweaks and linting --- diff --git a/examples/all-in-one-no-pm.py b/examples/all-in-one-no-pm.py index d9b1069..de8ab06 100755 --- a/examples/all-in-one-no-pm.py +++ b/examples/all-in-one-no-pm.py @@ -67,8 +67,9 @@ def display_text(variable, data, unit): # Maintain length of list values[variable] = values[variable][1:] + [data] # Scale the values for the variable between 0 and 1 - colours = [(v - min(values[variable]) + 1) / (max(values[variable]) - - min(values[variable]) + 1) for v in values[variable]] + vmin = min(values[variable]) + vmax = max(values[variable]) + colours = [(v - vmin + 1) / (vmax - vmin + 1) for v in values[variable]] # Format the variable name and value message = "{}: {:.1f} {}".format(variable[:4], data, unit) logging.info(message) @@ -76,14 +77,12 @@ def display_text(variable, data, unit): for i in range(len(colours)): # Convert the values to colours from red to blue colour = (1.0 - colours[i]) * 0.6 - r, g, b = [int(x * 255.0) for x in colorsys.hsv_to_rgb(colour, - 1.0, 1.0)] + r, g, b = [int(x * 255.0) for x in colorsys.hsv_to_rgb(colour, 1.0, 1.0)] # Draw a 1-pixel wide rectangle of colour - draw.rectangle((i, top_pos, i+1, HEIGHT), (r, g, b)) + draw.rectangle((i, top_pos, i + 1, HEIGHT), (r, g, b)) # Draw a line graph in black - line_y = HEIGHT - (top_pos + (colours[i] * (HEIGHT - top_pos)))\ - + top_pos - draw.rectangle((i, line_y, i+1, line_y+1), (0, 0, 0)) + line_y = HEIGHT - (top_pos + (colours[i] * (HEIGHT - top_pos))) + top_pos + draw.rectangle((i, line_y, i + 1, line_y + 1), (0, 0, 0)) # Write the text at the top in black draw.text((0, 0), message, font=font, fill=(0, 0, 0)) st7735.display(img) diff --git a/examples/all-in-one.py b/examples/all-in-one.py index c0423e6..6dda607 100755 --- a/examples/all-in-one.py +++ b/examples/all-in-one.py @@ -2,7 +2,6 @@ import time import colorsys -import os import sys import ST7735 try: @@ -72,8 +71,9 @@ def display_text(variable, data, unit): # Maintain length of list values[variable] = values[variable][1:] + [data] # Scale the values for the variable between 0 and 1 - colours = [(v - min(values[variable]) + 1) / (max(values[variable]) - - min(values[variable]) + 1) for v in values[variable]] + vmin = min(values[variable]) + vmax = max(values[variable]) + colours = [(v - vmin + 1) / (vmax - vmin + 1) for v in values[variable]] # Format the variable name and value message = "{}: {:.1f} {}".format(variable[:4], data, unit) logging.info(message) @@ -81,14 +81,12 @@ def display_text(variable, data, unit): for i in range(len(colours)): # Convert the values to colours from red to blue colour = (1.0 - colours[i]) * 0.6 - r, g, b = [int(x * 255.0) for x in colorsys.hsv_to_rgb(colour, - 1.0, 1.0)] + r, g, b = [int(x * 255.0) for x in colorsys.hsv_to_rgb(colour, 1.0, 1.0)] # Draw a 1-pixel wide rectangle of colour - draw.rectangle((i, top_pos, i+1, HEIGHT), (r, g, b)) + draw.rectangle((i, top_pos, i + 1, HEIGHT), (r, g, b)) # Draw a line graph in black - line_y = HEIGHT - (top_pos + (colours[i] * (HEIGHT - top_pos)))\ - + top_pos - draw.rectangle((i, line_y, i+1, line_y+1), (0, 0, 0)) + line_y = HEIGHT - (top_pos + (colours[i] * (HEIGHT - top_pos))) + top_pos + draw.rectangle((i, line_y, i + 1, line_y + 1), (0, 0, 0)) # Write the text at the top in black draw.text((0, 0), message, font=font, fill=(0, 0, 0)) st7735.display(img) diff --git a/examples/combined.py b/examples/combined.py index c2fd397..4b8fbdd 100755 --- a/examples/combined.py +++ b/examples/combined.py @@ -2,7 +2,6 @@ import time import colorsys -import os import sys import ST7735 try: @@ -107,23 +106,23 @@ units = ["C", # with NO WARRANTY. The authors of this example code claim # NO RESPONSIBILITY if reliance on the following values or this # code in general leads to ANY DAMAGES or DEATH. -limits = [[4,18,28,35], - [250,650,1013.25,1015], - [20,30,60,70], - [-1,-1,30000,100000], - [-1,-1,40,50], - [-1,-1,450,550], - [-1,-1,200,300], - [-1,-1,50,100], - [-1,-1,50,100], - [-1,-1,50,100]] +limits = [[4, 18, 28, 35], + [250, 650, 1013.25, 1015], + [20, 30, 60, 70], + [-1, -1, 30000, 100000], + [-1, -1, 40, 50], + [-1, -1, 450, 550], + [-1, -1, 200, 300], + [-1, -1, 50, 100], + [-1, -1, 50, 100], + [-1, -1, 50, 100]] # RGB palette for values on the combined screen -palette = [(0,0,255), # Dangerously Low - (0,255,255), # Low - (0,255,0), # Normal - (255,255,0), # High - (255,0,0)] # Dangerously High +palette = [(0, 0, 255), # Dangerously Low + (0, 255, 255), # Low + (0, 255, 0), # Normal + (255, 255, 0), # High + (255, 0, 0)] # Dangerously High values = {} @@ -133,8 +132,9 @@ def display_text(variable, data, unit): # Maintain length of list values[variable] = values[variable][1:] + [data] # Scale the values for the variable between 0 and 1 - colours = [(v - min(values[variable]) + 1) / (max(values[variable]) - - min(values[variable]) + 1) for v in values[variable]] + vmin = min(values[variable]) + vmax = max(values[variable]) + colours = [(v - vmin + 1) / (vmax - vmin + 1) for v in values[variable]] # Format the variable name and value message = "{}: {:.1f} {}".format(variable[:4], data, unit) logging.info(message) @@ -142,18 +142,17 @@ def display_text(variable, data, unit): for i in range(len(colours)): # Convert the values to colours from red to blue colour = (1.0 - colours[i]) * 0.6 - r, g, b = [int(x * 255.0) for x in colorsys.hsv_to_rgb(colour, - 1.0, 1.0)] + r, g, b = [int(x * 255.0) for x in colorsys.hsv_to_rgb(colour, 1.0, 1.0)] # Draw a 1-pixel wide rectangle of colour - draw.rectangle((i, top_pos, i+1, HEIGHT), (r, g, b)) + draw.rectangle((i, top_pos, i + 1, HEIGHT), (r, g, b)) # Draw a line graph in black - line_y = HEIGHT - (top_pos + (colours[i] * (HEIGHT - top_pos)))\ - + top_pos - draw.rectangle((i, line_y, i+1, line_y+1), (0, 0, 0)) + line_y = HEIGHT - (top_pos + (colours[i] * (HEIGHT - top_pos))) + top_pos + draw.rectangle((i, line_y, i + 1, line_y + 1), (0, 0, 0)) # Write the text at the top in black draw.text((0, 0), message, font=font, fill=(0, 0, 0)) st7735.display(img) + # Saves the data to be used in the graphs later and prints to the log def save_data(idx, data): variable = variables[idx] @@ -168,24 +167,23 @@ def save_data(idx, data): def display_everything(): draw.rectangle((0, 0, WIDTH, HEIGHT), (0, 0, 0)) column_count = 2 - row_count = (len(variables)/column_count) + row_count = (len(variables) / column_count) for i in range(len(variables)): variable = variables[i] data_value = values[variable][-1] unit = units[i] - x = x_offset + ((WIDTH/column_count) * (i / row_count)) - y = y_offset + ((HEIGHT/row_count) * (i % row_count)) + x = x_offset + ((WIDTH / column_count) * (i / row_count)) + y = y_offset + ((HEIGHT / row_count) * (i % row_count)) message = "{}: {:.1f} {}".format(variable[:4], data_value, unit) lim = limits[i] rgb = palette[0] for j in range(len(lim)): if data_value > lim[j]: - rgb = palette[j+1] + rgb = palette[j + 1] draw.text((x, y), message, font=smallfont, fill=rgb) st7735.display(img) - # Get the temperature of the CPU for compensation def get_cpu_temperature(): process = Popen(['vcgencmd', 'measure_temp'], stdout=PIPE, universal_newlines=True) @@ -193,156 +191,158 @@ def get_cpu_temperature(): return float(output[output.index('=') + 1:output.rindex("'")]) -# Tuning factor for compensation. Decrease this number to adjust the -# temperature down, and increase to adjust up -factor = 2.25 - -cpu_temps = [get_cpu_temperature()] * 5 - -delay = 0.5 # Debounce the proximity tap -mode = 10 # The starting mode -last_page = 0 -light = 1 +def main(): + # Tuning factor for compensation. Decrease this number to adjust the + # temperature down, and increase to adjust up + factor = 2.25 + + cpu_temps = [get_cpu_temperature()] * 5 + + delay = 0.5 # Debounce the proximity tap + mode = 10 # The starting mode + last_page = 0 + + for v in variables: + values[v] = [1] * WIDTH + + # The main loop + try: + while True: + proximity = ltr559.get_proximity() + + # If the proximity crosses the threshold, toggle the mode + if proximity > 1500 and time.time() - last_page > delay: + mode += 1 + mode %= (len(variables) + 1) + last_page = time.time() + + # One mode for each variable + if mode == 0: + # variable = "temperature" + unit = "C" + cpu_temp = get_cpu_temperature() + # Smooth out with some averaging to decrease jitter + cpu_temps = cpu_temps[1:] + [cpu_temp] + avg_cpu_temp = sum(cpu_temps) / float(len(cpu_temps)) + raw_temp = bme280.get_temperature() + data = raw_temp - ((avg_cpu_temp - raw_temp) / factor) + display_text(variables[mode], data, unit) -for v in variables: - values[v] = [1] * WIDTH + if mode == 1: + # variable = "pressure" + unit = "hPa" + data = bme280.get_pressure() + display_text(variables[mode], data, unit) -# The main loop -try: - while True: - proximity = ltr559.get_proximity() - - # If the proximity crosses the threshold, toggle the mode - if proximity > 1500 and time.time() - last_page > delay: - mode += 1 - mode %= (len(variables)+1) - last_page = time.time() - - # One mode for each variable - if mode == 0: - # variable = "temperature" - unit = "C" - cpu_temp = get_cpu_temperature() - # Smooth out with some averaging to decrease jitter - cpu_temps = cpu_temps[1:] + [cpu_temp] - avg_cpu_temp = sum(cpu_temps) / float(len(cpu_temps)) - raw_temp = bme280.get_temperature() - data = raw_temp - ((avg_cpu_temp - raw_temp) / factor) - display_text(variables[mode], data, unit) - - if mode == 1: - # variable = "pressure" - unit = "hPa" - data = bme280.get_pressure() - display_text(variables[mode], data, unit) - - if mode == 2: - # variable = "humidity" - unit = "%" - data = bme280.get_humidity() - display_text(variables[mode], data, unit) - - if mode == 3: - # variable = "light" - unit = "Lux" - if proximity < 10: - data = ltr559.get_lux() - else: - data = 1 - display_text(variables[mode], data, unit) - - if mode == 4: - # variable = "oxidised" - unit = "kO" - data = gas.read_all() - data = data.oxidising / 1000 - display_text(variables[mode], data, unit) - - if mode == 5: - # variable = "reduced" - unit = "kO" - data = gas.read_all() - data = data.reducing / 1000 - display_text(variables[mode], data, unit) - - if mode == 6: - # variable = "nh3" - unit = "kO" - data = gas.read_all() - data = data.nh3 / 1000 - display_text(variables[mode], data, unit) - - if mode == 7: - # variable = "pm1" - unit = "ug/m3" - try: - data = pms5003.read() - except pmsReadTimeoutError: - logging.warn("Failed to read PMS5003") - else: - data = float(data.pm_ug_per_m3(1.0)) + if mode == 2: + # variable = "humidity" + unit = "%" + data = bme280.get_humidity() display_text(variables[mode], data, unit) - if mode == 8: - # variable = "pm25" - unit = "ug/m3" - try: - data = pms5003.read() - except pmsReadTimeoutError: - logging.warn("Failed to read PMS5003") - else: - data = float(data.pm_ug_per_m3(2.5)) + if mode == 3: + # variable = "light" + unit = "Lux" + if proximity < 10: + data = ltr559.get_lux() + else: + data = 1 display_text(variables[mode], data, unit) - if mode == 9: - # variable = "pm10" - unit = "ug/m3" - try: - data = pms5003.read() - except pmsReadTimeoutError: - logging.warn("Failed to read PMS5003") - else: - data = float(data.pm_ug_per_m3(10)) + if mode == 4: + # variable = "oxidised" + unit = "kO" + data = gas.read_all() + data = data.oxidising / 1000 display_text(variables[mode], data, unit) - if mode == 10: - # Everything on one screen - cpu_temp = get_cpu_temperature() - # Smooth out with some averaging to decrease jitter - cpu_temps = cpu_temps[1:] + [cpu_temp] - avg_cpu_temp = sum(cpu_temps) / float(len(cpu_temps)) - raw_temp = bme280.get_temperature() - raw_data = raw_temp - ((avg_cpu_temp - raw_temp) / factor) - save_data(0, raw_data) - display_everything() - raw_data = bme280.get_pressure() - save_data(1, raw_data) - display_everything() - raw_data = bme280.get_humidity() - save_data(2, raw_data) - if proximity < 10: - raw_data = ltr559.get_lux() - else: - raw_data = 1 - save_data(3, raw_data) - display_everything() - gas_data = gas.read_all() - save_data(4, gas_data.oxidising / 1000) - save_data(5, gas_data.reducing / 1000) - save_data(6, gas_data.nh3 / 1000) - display_everything() - pms_data = None - try: - pms_data = pms5003.read() - except pmsReadTimeoutError: - logging.warn("Failed to read PMS5003") - else: - save_data(7, float(pms_data.pm_ug_per_m3(1.0))) - save_data(8, float(pms_data.pm_ug_per_m3(2.5))) - save_data(9, float(pms_data.pm_ug_per_m3(10))) - display_everything() + if mode == 5: + # variable = "reduced" + unit = "kO" + data = gas.read_all() + data = data.reducing / 1000 + display_text(variables[mode], data, unit) + if mode == 6: + # variable = "nh3" + unit = "kO" + data = gas.read_all() + data = data.nh3 / 1000 + display_text(variables[mode], data, unit) -# Exit cleanly -except KeyboardInterrupt: - sys.exit(0) + if mode == 7: + # variable = "pm1" + unit = "ug/m3" + try: + data = pms5003.read() + except pmsReadTimeoutError: + logging.warn("Failed to read PMS5003") + else: + data = float(data.pm_ug_per_m3(1.0)) + display_text(variables[mode], data, unit) + + if mode == 8: + # variable = "pm25" + unit = "ug/m3" + try: + data = pms5003.read() + except pmsReadTimeoutError: + logging.warn("Failed to read PMS5003") + else: + data = float(data.pm_ug_per_m3(2.5)) + display_text(variables[mode], data, unit) + + if mode == 9: + # variable = "pm10" + unit = "ug/m3" + try: + data = pms5003.read() + except pmsReadTimeoutError: + logging.warn("Failed to read PMS5003") + else: + data = float(data.pm_ug_per_m3(10)) + display_text(variables[mode], data, unit) + if mode == 10: + # Everything on one screen + cpu_temp = get_cpu_temperature() + # Smooth out with some averaging to decrease jitter + cpu_temps = cpu_temps[1:] + [cpu_temp] + avg_cpu_temp = sum(cpu_temps) / float(len(cpu_temps)) + raw_temp = bme280.get_temperature() + raw_data = raw_temp - ((avg_cpu_temp - raw_temp) / factor) + save_data(0, raw_data) + display_everything() + raw_data = bme280.get_pressure() + save_data(1, raw_data) + display_everything() + raw_data = bme280.get_humidity() + save_data(2, raw_data) + if proximity < 10: + raw_data = ltr559.get_lux() + else: + raw_data = 1 + save_data(3, raw_data) + display_everything() + gas_data = gas.read_all() + save_data(4, gas_data.oxidising / 1000) + save_data(5, gas_data.reducing / 1000) + save_data(6, gas_data.nh3 / 1000) + display_everything() + pms_data = None + try: + pms_data = pms5003.read() + except pmsReadTimeoutError: + logging.warn("Failed to read PMS5003") + else: + save_data(7, float(pms_data.pm_ug_per_m3(1.0))) + save_data(8, float(pms_data.pm_ug_per_m3(2.5))) + save_data(9, float(pms_data.pm_ug_per_m3(10))) + display_everything() + + # Exit cleanly + except KeyboardInterrupt: + sys.exit(0) + + +if __name__ == "__main__": + main() diff --git a/examples/luftdaten.py b/examples/luftdaten.py index d2d6562..84f1117 100755 --- a/examples/luftdaten.py +++ b/examples/luftdaten.py @@ -115,32 +115,35 @@ def send_to_luftdaten(values, id): pm_values = dict(i for i in values.items() if i[0].startswith("P")) temp_values = dict(i for i in values.items() if not i[0].startswith("P")) - resp_1 = requests.post("https://api.luftdaten.info/v1/push-sensor-data/", - json={ - "software_version": "enviro-plus 0.0.1", - "sensordatavalues": [{"value_type": key, "value": val} for - key, val in pm_values.items()] - }, - headers={ - "X-PIN": "1", - "X-Sensor": id, - "Content-Type": "application/json", - "cache-control": "no-cache" - } + pm_values_json = [{"value_type": key, "value": val} for key, val in pm_values.items()] + temp_values_json = [{"value_type": key, "value": val} for key, val in temp_values.items()] + + resp_1 = requests.post( + "https://api.luftdaten.info/v1/push-sensor-data/", + json={ + "software_version": "enviro-plus 0.0.1", + "sensordatavalues": pm_values_json + }, + headers={ + "X-PIN": "1", + "X-Sensor": id, + "Content-Type": "application/json", + "cache-control": "no-cache" + } ) - resp_2 = requests.post("https://api.luftdaten.info/v1/push-sensor-data/", - json={ - "software_version": "enviro-plus 0.0.1", - "sensordatavalues": [{"value_type": key, "value": val} for - key, val in temp_values.items()] - }, - headers={ - "X-PIN": "11", - "X-Sensor": id, - "Content-Type": "application/json", - "cache-control": "no-cache" - } + resp_2 = requests.post( + "https://api.luftdaten.info/v1/push-sensor-data/", + json={ + "software_version": "enviro-plus 0.0.1", + "sensordatavalues": temp_values_json + }, + headers={ + "X-PIN": "11", + "X-Sensor": id, + "Content-Type": "application/json", + "cache-control": "no-cache" + } ) if resp_1.ok and resp_2.ok: diff --git a/examples/noise-amps-at-freqs.py b/examples/noise-amps-at-freqs.py index 8b1ddd5..4c14c58 100755 --- a/examples/noise-amps-at-freqs.py +++ b/examples/noise-amps-at-freqs.py @@ -15,11 +15,11 @@ Press Ctrl+C to exit! noise = Noise() disp = ST7735.ST7735( - port=0, - cs=ST7735.BG_SPI_CS_FRONT, - dc=9, - backlight=12, - rotation=90) + port=0, + cs=ST7735.BG_SPI_CS_FRONT, + dc=9, + backlight=12, + rotation=90) disp.begin() diff --git a/examples/noise-profile.py b/examples/noise-profile.py index 1afdff5..4084439 100755 --- a/examples/noise-profile.py +++ b/examples/noise-profile.py @@ -13,11 +13,11 @@ Press Ctrl+C to exit! noise = Noise() disp = ST7735.ST7735( - port=0, - cs=ST7735.BG_SPI_CS_FRONT, - dc=9, - backlight=12, - rotation=90) + port=0, + cs=ST7735.BG_SPI_CS_FRONT, + dc=9, + backlight=12, + rotation=90) disp.begin() diff --git a/examples/weather-and-light.py b/examples/weather-and-light.py index a26ec7b..bccf7cc 100755 --- a/examples/weather-and-light.py +++ b/examples/weather-and-light.py @@ -109,19 +109,19 @@ def sun_moon_time(city_name, time_zone): if sunrise_today < local_dt < sunset_today: day = True period = sunset_today - sunrise_today - mid = sunrise_today + (period / 2) + # mid = sunrise_today + (period / 2) progress = local_dt - sunrise_today elif local_dt > sunset_today: day = False period = sunrise_tomorrow - sunset_today - mid = sunset_today + (period / 2) + # mid = sunset_today + (period / 2) progress = local_dt - sunset_today else: day = False period = sunrise_today - sunset_yesterday - mid = sunset_yesterday + (period / 2) + # mid = sunset_yesterday + (period / 2) progress = local_dt - sunset_yesterday # Convert time deltas to seconds @@ -151,7 +151,7 @@ def draw_background(progress, period, day): # New image for background colour img = Image.new('RGBA', (WIDTH, HEIGHT), color=background) - draw = ImageDraw.Draw(img) + # draw = ImageDraw.Draw(img) # New image for sun/moon overlay overlay = Image.new('RGBA', (WIDTH, HEIGHT), color=(0, 0, 0, 0)) @@ -216,12 +216,12 @@ def analyse_pressure(pressure, t): slope = line[0][0] intercept = line[0][1] variance = numpy.var(pressure_vals) - residuals = numpy.var([(slope * x + intercept - y) for x, y in zip(time_vals, pressure_vals)]) + residuals = numpy.var([(slope * x + intercept - y) for x, y in zip(time_vals, pressure_vals)]) r_squared = 1 - residuals / variance # Calculate change in pressure per hour change_per_hour = slope * 60 * 60 - variance_per_hour = variance * 60 * 60 + # variance_per_hour = variance * 60 * 60 mean_pressure = numpy.mean(pressure_vals) @@ -244,10 +244,10 @@ def analyse_pressure(pressure, t): change_per_hour = 0 trend = "-" -# time.sleep(interval) - + # time.sleep(interval) return (mean_pressure, change_per_hour, trend) + def describe_pressure(pressure): """Convert pressure into barometer-type description.""" if pressure < 970: @@ -385,7 +385,7 @@ while True: else: range_string = "------" img = overlay_text(img, (68, 18 + spacing), range_string, font_sm, align_right=True, rectangle=True) - temp_icon = Image.open(path + "/icons/temperature.png") + temp_icon = Image.open(f"{path}/icons/temperature.png") img.paste(temp_icon, (margin, 18), mask=temp_icon) # Humidity @@ -396,7 +396,7 @@ while True: spacing = font_lg.getsize(humidity_string)[1] + 1 humidity_desc = describe_humidity(corr_humidity).upper() img = overlay_text(img, (68, 48 + spacing), humidity_desc, font_sm, align_right=True, rectangle=True) - humidity_icon = Image.open(path + "/icons/humidity-" + humidity_desc.lower() + ".png") + humidity_icon = Image.open(f"{path}/icons/humidity-{humidity_desc.lower()}.png") img.paste(humidity_icon, (margin, 48), mask=humidity_icon) # Light @@ -406,7 +406,7 @@ while True: spacing = font_lg.getsize(light_string.replace(",", ""))[1] + 1 light_desc = describe_light(light).upper() img = overlay_text(img, (WIDTH - margin - 1, 18 + spacing), light_desc, font_sm, align_right=True, rectangle=True) - light_icon = Image.open(path + "/icons/bulb-" + light_desc.lower() + ".png") + light_icon = Image.open(f"{path}/icons/bulb-{light_desc.lower()}.png") img.paste(humidity_icon, (80, 18), mask=light_icon) # Pressure @@ -418,7 +418,7 @@ while True: pressure_desc = describe_pressure(mean_pressure).upper() spacing = font_lg.getsize(pressure_string.replace(",", ""))[1] + 1 img = overlay_text(img, (WIDTH - margin - 1, 48 + spacing), pressure_desc, font_sm, align_right=True, rectangle=True) - pressure_icon = Image.open(path + "/icons/weather-" + pressure_desc.lower() + ".png") + pressure_icon = Image.open(f"{path}/icons/weather-{pressure_desc.lower()}.png") img.paste(pressure_icon, (80, 48), mask=pressure_icon) # Display image diff --git a/library/setup.cfg b/library/setup.cfg index 83f89fa..af306be 100644 --- a/library/setup.cfg +++ b/library/setup.cfg @@ -4,7 +4,7 @@ name = enviroplus version = 0.0.3 author = Philip Howard author_email = phil@pimoroni.com -description = Enviro pHAT Plus environmental monitoring add-on for Raspberry Pi" +description = Enviro pHAT Plus environmental monitoring add-on for Raspberry Pi long_description = file: README.rst keywords = Raspberry Pi url = https://www.pimoroni.com @@ -19,7 +19,6 @@ classifiers = Operating System :: POSIX :: Linux License :: OSI Approved :: MIT License Intended Audience :: Developers - Programming Language :: Python :: 2.6 Programming Language :: Python :: 2.7 Programming Language :: Python :: 3 Topic :: Software Development @@ -27,6 +26,7 @@ classifiers = Topic :: System :: Hardware [options] +packages = enviroplus install_requires = pimoroni-bme280 pms5003 diff --git a/library/setup.py b/library/setup.py index 784db51..40d6dbc 100755 --- a/library/setup.py +++ b/library/setup.py @@ -30,7 +30,4 @@ minimum_version = parse_version('30.4.0') if parse_version(__version__) < minimum_version: raise RuntimeError("Package setuptools must be at least version {}".format(minimum_version)) -setup( - packages=['enviroplus'], - install_requires=['setuptools>={}'.format(minimum_version), 'pimoroni-bme280', 'pms5003', 'ltr559', 'st7735', 'ads1015', 'fonts', 'font-roboto', 'astral', 'pytz'] -) +setup() diff --git a/library/tests/conftest.py b/library/tests/conftest.py new file mode 100644 index 0000000..b026172 --- /dev/null +++ b/library/tests/conftest.py @@ -0,0 +1,73 @@ +"""Test configuration. +These allow the mocking of various Python modules +that might otherwise have runtime side-effects. +""" +import sys +import mock +import pytest +from i2cdevice import MockSMBus + + +class SMBusFakeDevice(MockSMBus): + def __init__(self, i2c_bus): + MockSMBus.__init__(self, i2c_bus) + self.regs[0x00:0x01] = 0x0f, 0x00 + + +@pytest.fixture(scope='function', autouse=False) +def GPIO(): + """Mock RPi.GPIO module.""" + GPIO = mock.MagicMock() + # Fudge for Python < 37 (possibly earlier) + sys.modules['RPi'] = mock.Mock() + sys.modules['RPi'].GPIO = GPIO + sys.modules['RPi.GPIO'] = GPIO + yield GPIO + del sys.modules['RPi'] + del sys.modules['RPi.GPIO'] + + +@pytest.fixture(scope='function', autouse=False) +def spidev(): + """Mock spidev module.""" + spidev = mock.MagicMock() + sys.modules['spidev'] = spidev + yield spidev + del sys.modules['spidev'] + + +@pytest.fixture(scope='function', autouse=False) +def smbus(): + """Mock smbus module.""" + smbus = mock.MagicMock() + smbus.SMBus = SMBusFakeDevice + sys.modules['smbus'] = smbus + yield smbus + del sys.modules['smbus'] + + +@pytest.fixture(scope='function', autouse=False) +def atexit(): + """Mock atexit module.""" + atexit = mock.MagicMock() + sys.modules['atexit'] = atexit + yield atexit + del sys.modules['atexit'] + + +@pytest.fixture(scope='function', autouse=False) +def sounddevice(): + """Mock sounddevice module.""" + sounddevice = mock.MagicMock() + sys.modules['sounddevice'] = sounddevice + yield sounddevice + del sys.modules['sounddevice'] + + +@pytest.fixture(scope='function', autouse=False) +def numpy(): + """Mock numpy module.""" + numpy = mock.MagicMock() + sys.modules['numpy'] = numpy + yield numpy + del sys.modules['numpy'] diff --git a/library/tests/test_noise.py b/library/tests/test_noise.py new file mode 100644 index 0000000..c93f8cc --- /dev/null +++ b/library/tests/test_noise.py @@ -0,0 +1,78 @@ +import sys +import mock +import pytest + + +def force_reimport(module): + """Force the module under test to be re-imported. + + Because pytest runs all tests within the same scope (this makes me cry) + we have to do some manual housekeeping to avoid tests polluting each other. + + Since conftest.py already does some sys.modules mangling I see no reason not to + do the same thing here. + """ + if "." in module: + steps = module.split(".") + else: + steps = [module] + + for i in range(len(steps)): + module = ".".join(steps[0:i + 1]) + try: + del sys.modules[module] + except KeyError: + pass + + +def test_noise_setup(sounddevice, numpy): + force_reimport('enviroplus.noise') + from enviroplus.noise import Noise + + noise = Noise(sample_rate=16000, duration=0.1) + del noise + + +def test_noise_get_amplitudes_at_frequency_ranges(sounddevice, numpy): + # Ippity zippidy what is this farce + # a curious function that makes my tests pass? + force_reimport('enviroplus.noise') + from enviroplus.noise import Noise + + noise = Noise(sample_rate=16000, duration=0.1) + noise.get_amplitudes_at_frequency_ranges([ + (100, 500), + (501, 1000) + ]) + + sounddevice.rec.assert_called_with(0.1 * 16000, samplerate=16000, blocking=True, channels=1, dtype='float64') + + +def test_noise_get_noise_profile(sounddevice, numpy): + # Ippity zippidy what is this farce + # a curious function that makes my tests pass? + force_reimport('enviroplus.noise') + from enviroplus.noise import Noise + + noise = Noise(sample_rate=16000, duration=0.1) + amp_low, amp_mid, amp_high, amp_total = noise.get_noise_profile( + noise_floor=100, + low=0.12, + mid=0.36, + high=None) + + sounddevice.rec.assert_called_with(0.1 * 16000, samplerate=16000, blocking=True, channels=1, dtype='float64') + + +def test_get_amplitude_at_frequency_range(sounddevice, numpy): + # Ippity zippidy what is this farce + # a curious function that makes my tests pass? + force_reimport('enviroplus.noise') + from enviroplus.noise import Noise + + noise = Noise(sample_rate=16000, duration=0.1) + + noise.get_amplitude_at_frequency_range(0, 8000) + + with pytest.raises(ValueError): + noise.get_amplitude_at_frequency_range(0, 16000) diff --git a/library/tests/test_setup.py b/library/tests/test_setup.py index 7c25d94..6b6658c 100644 --- a/library/tests/test_setup.py +++ b/library/tests/test_setup.py @@ -1,32 +1,37 @@ import sys import mock -from i2cdevice import MockSMBus -class SMBusFakeDevice(MockSMBus): - def __init__(self, i2c_bus): - MockSMBus.__init__(self, i2c_bus) - self.regs[0x00:0x01] = 0x0f, 0x00 +def force_reimport(module): + """Force the module under test to be re-imported. + Because pytest runs all tests within the same scope (this makes me cry) + we have to do some manual housekeeping to avoid tests polluting each other. -def test_gas_setup(): - sys.modules['RPi'] = mock.Mock() - sys.modules['RPi.GPIO'] = mock.Mock() - smbus = mock.Mock() - smbus.SMBus = SMBusFakeDevice - sys.modules['smbus'] = smbus + Since conftest.py already does some sys.modules mangling I see no reason not to + do the same thing here. + """ + if "." in module: + steps = module.split(".") + else: + steps = [module] + + for i in range(len(steps)): + module = ".".join(steps[0:i + 1]) + try: + del sys.modules[module] + except KeyError: + pass + + +def test_gas_setup(GPIO, smbus): from enviroplus import gas gas._is_setup = False gas.setup() gas.setup() -def test_gas_read_all(): - sys.modules['RPi'] = mock.Mock() - sys.modules['RPi.GPIO'] = mock.Mock() - smbus = mock.Mock() - smbus.SMBus = SMBusFakeDevice - sys.modules['smbus'] = smbus +def test_gas_read_all(GPIO, smbus): from enviroplus import gas gas._is_setup = False result = gas.read_all() @@ -43,12 +48,7 @@ def test_gas_read_all(): assert "Oxidising" in str(result) -def test_gas_read_each(): - sys.modules['RPi'] = mock.Mock() - sys.modules['RPi.GPIO'] = mock.Mock() - smbus = mock.Mock() - smbus.SMBus = SMBusFakeDevice - sys.modules['smbus'] = smbus +def test_gas_read_each(GPIO, smbus): from enviroplus import gas gas._is_setup = False @@ -57,12 +57,7 @@ def test_gas_read_each(): assert int(gas.read_nh3()) == 16813 -def test_gas_read_adc(): - sys.modules['RPi'] = mock.Mock() - sys.modules['RPi.GPIO'] = mock.Mock() - smbus = mock.Mock() - smbus.SMBus = SMBusFakeDevice - sys.modules['smbus'] = smbus +def test_gas_read_adc(GPIO, smbus): from enviroplus import gas gas._is_setup = False @@ -71,28 +66,28 @@ def test_gas_read_adc(): assert gas.read_adc() == 0.255 -def test_gas_read_adc_default_gain(): - sys.modules['RPi'] = mock.Mock() - sys.modules['RPi.GPIO'] = mock.Mock() - smbus = mock.Mock() - smbus.SMBus = SMBusFakeDevice - sys.modules['smbus'] = smbus +def test_gas_read_adc_default_gain(GPIO, smbus): from enviroplus import gas gas._is_setup = False gas.enable_adc(True) - assert gas.read_adc() == 0.255 + gas.set_adc_gain(gas.MICS6814_GAIN) + assert gas.read_adc() == 0.765 -def test_gas_read_adc_str(): - sys.modules['RPi'] = mock.Mock() - sys.modules['RPi.GPIO'] = mock.Mock() - smbus = mock.Mock() - smbus.SMBus = SMBusFakeDevice - sys.modules['smbus'] = smbus +def test_gas_read_adc_str(GPIO, smbus): from enviroplus import gas gas._is_setup = False gas.enable_adc(True) gas.set_adc_gain(2.048) assert 'ADC' in str(gas.read_all()) + + +def test_gas_cleanup(GPIO, smbus): + force_reimport('enviroplus.gas') + from enviroplus import gas + + gas.cleanup() + + GPIO.output.assert_called_with(gas.MICS6814_HEATER_PIN, 0)