def __init__(self, *args, **configuration): self.old_weatherdata = None self.args = args self.configuration = configuration self.interface = WeatherVaneInterface(*args, **configuration) self.display = Display(self.interface, **configuration['display']) logging.info("Using " + str(self.interface)) self.wd = None self.counter = 0 self.interval = configuration['interval'] self.sleep_time = configuration['sleep-time'] self.start_collection_time = datetime.datetime.now() self.end_collection_time = datetime.datetime.now() self.reached = False
def test_init(self, mock_class): result = WeatherVaneInterface(**test_config.config) expected = 'WeatherVaneInterface(channel=0, frequency=250000)' self.assertEqual( expected, str(result), 'Weather Vane Interface failed to be setup correctly - %s, %s' % (expected, result))
class WeatherVane(object): def __init__(self, *args, **configuration): self.old_weatherdata = None self.args = args self.configuration = configuration self.interface = WeatherVaneInterface(*args, **configuration) self.display = Display(self.interface, **configuration['display']) logging.info("Using " + str(self.interface)) self.wd = None self.counter = 0 self.interval = configuration['interval'] self.sleep_time = configuration['sleep-time'] self.start_collection_time = datetime.datetime.now() self.end_collection_time = datetime.datetime.now() self.reached = False def test_mode(self): """ Test mode is used to output a predicable sequence of bytes to the output pins. The program will send 3 bytes every second to the pins. - Byte 1: an increasing counter (modulo 255) - Byte 2: a decreasing counter (idem) - Byte 3: switches between 0x55 and 0xAA """ logging.info("Starting test mode") interface = TestInterface(channel=0, frequency=25000) counter = 0 while True: counter += 1 if counter % 2: test = 0x55 else: test = 0xAA data = [counter % 255, (255 - counter) % 255, test] interface.send(data) time.sleep(1) def check_selected_station(self, selected_station): """Check if another station is selected and change it when it has Determine whether the selected station has changed on the interface. If it has changed, then immediately start using data from this new station and return the id of the newly selected station. In the config-file you can specify a list of stations where data may be collected from. You can choose the specific station by also specifying on or more pins on the raspberry that together form a bitwise number. E.g you want to select one of four stations. You will need to specify 2 pins on the raspberry (2**2) that select on of these four stations. If pin 0 is high and pin 1 is low, you will select station 2. If both pins are high, you will select station 3, etc. Side effect: reset counter to 0 if another station is selected @param selected_station: the currently used station @return: either the id of the new station or the id of the station already in use """ station_id = self.interface.selected_station if station_id != selected_station: # reset if a new station is selected self.counter = 0 logging.info("New station selected: {}".format(station_id)) return station_id def start_data_collection(self, pipe_end): """Side effect: reset counter to 0 @param pipe_end_1: @return: """ self.counter = 0 arguments = [pipe_end] arguments.extend(self.args) p = Process(target=fetch_weather_data, args=arguments, kwargs=self.configuration) p.start() logging.debug('Retrieving data') def send_data(self): if self.old_weatherdata: wd = self.interpolate(self.old_weatherdata, self.wd, self.interval) self.interface.send(wd) else: self.interface.send(self.wd) def retrieve_data(self, pipe_end_2): logging.info('Data available:') self.end_collection_time = datetime.datetime.now() self.reached = False logging.info('Data retrieval including parsing took {}'.format( self.end_collection_time - self.start_collection_time)) self.old_weatherdata, self.wd = self.wd, pipe_end_2.recv() logging.info(pprint.pformat(self.wd)) def start_data_collection_and_timer(self, pipe_end_1): self.start_collection_time = datetime.datetime.now() self.start_data_collection(pipe_end_1) def log_heartbeat(self): logging.debug('Heartbeat-{}'.format(self.counter)) def set_logger(self): weathervane_logger = logging.getLogger('') weathervane_logger.setLevel(logging.INFO) handler = logging.handlers.TimedRotatingFileHandler(filename="weathervane.log", when="midnight", interval=1, backupCount=7) formatter = logging.Formatter("%(asctime)s:%(levelname)s:%(module)s:%(message)s") handler.setFormatter(formatter) weathervane_logger.addHandler(handler) def interpolate(self, old_weatherdata, new_weatherdata, interval): if self.counter >= interval - 1: self.reached = True if new_weatherdata['error']: return new_weatherdata interpolated_wd = {} for key, old_value in list(old_weatherdata.items()): new_value = new_weatherdata.get(key, None) if not new_value: continue if key not in ['error', 'wind_direction', 'wind_direction', 'rain', 'barometric_trend'] and not self.reached: try: interpolated_value = float(old_value) + ( self.counter * (float(new_value) - float(old_value)) / interval) interpolated_wd[key] = interpolated_value except ValueError: interpolated_wd[key] = new_value except TypeError: interpolated_wd[key] = new_value else: interpolated_wd[key] = new_value return interpolated_wd def main(self): pipe_end_1, pipe_end_2 = Pipe() while True: self.display.tick() if (self.counter % 3) == 0: self.log_heartbeat() if (self.counter % self.interval) == 0: self.start_data_collection_and_timer(pipe_end_1) if pipe_end_2.poll(0): self.retrieve_data(pipe_end_2) if self.wd: self.send_data() self.counter += 1 time.sleep(self.sleep_time)
class WeatherVaneTest(unittest.TestCase): @patch('weathervane.weathervaneinterface.GPIO', autospec=True) def setUp(self, mock_class): self.interface = WeatherVaneInterface(**test_config.config) def noArguments(self, mock_class): self.assertRaises(KeyError, WeatherVaneInterface) def test_init(self, mock_class): result = WeatherVaneInterface(**test_config.config) expected = 'WeatherVaneInterface(channel=0, frequency=250000)' self.assertEqual( expected, str(result), 'Weather Vane Interface failed to be setup correctly - %s, %s' % (expected, result)) def test_bitstring_length(self, mock_class): weather_data = {'winddirection': 'NO'} self.interface.send(weather_data) self.assertEqual(len(self.interface.new_bit_string), 64) def test_toggle_bit_empty_data(self, mock_class): weather_data = {'winddirection': 'NO'} self.assertFalse(self.interface.data_changed) self.interface.send(weather_data) self.assertTrue(self.interface.data_changed) def test_bitstring_length_2(self, mock_class): weather_data = { 'winddirection': 'NO', 'windspeed': 10, 'windgusts': 15, 'windspeedBft': 6, 'airpressure': 1014, 'temperature': 20, 'feeltemperature': 21, 'humidity': 100 } self.interface.send(weather_data) self.assertEqual(len(self.interface.new_bit_string), 64) def test_toggle_bit(self, mock_class): weather_data = { 'winddirection': 'NO', 'windspeed': 0, 'windgusts': 0, 'windspeedBft': 1, 'airpressure': 900, 'temperature': 20, 'feeltemperature': 21, 'humidity': 100 } self.interface.send(weather_data) self.assertTrue(self.interface.data_changed) weather_data = { 'winddirection': 'O', 'windspeed': 0, 'windgusts': 0, 'windspeedBft': 1, 'airpressure': 900, 'temperature': 20, 'feeltemperature': 21, 'humidity': 100 } self.interface.send(weather_data) self.assertTrue(self.interface.data_changed) self.interface.send(weather_data) self.assertFalse(self.interface.data_changed) def test_error_winddirection(self, mock_class): weather_data = {'winddirection': 'A'} requested_data = [{'key': 'winddirection', 'length': '4'}] expected = {'winddirection': 0} result = self.interface.transmittable_data(weather_data, requested_data) self.assertEqual(expected, result) def test_air_pressure(self, mock_class): weather_data = {'airpressure': 901} requested_data = [{ 'key': 'airpressure', 'length': '8', 'max': '1155', 'min': '900', 'step': '1' }] expected = {'airpressure': 1} result = self.interface.transmittable_data(weather_data, requested_data) self.assertEqual(expected, result) def test_value_rounded(self, mock_class): weather_data = {'airpressure': 48.493} requested_data = [{'key': 'airpressure', 'min': '0', 'max': 63}] expected = {'airpressure': 48} result = self.interface.transmittable_data(weather_data, requested_data) self.assertEqual(expected, result) def test_value_too_low(self, mock_class): weather_data = {'windspeed': -1} requested_data = [{'key': 'windspeed', 'min': '0', 'max': 63}] expected = {'windspeed': 0} result = self.interface.transmittable_data(weather_data, requested_data) self.assertEqual(expected, result) def test_value_too_high(self, mock_class): weather_data = {'windspeed': 64} requested_data = [{'key': 'windspeed', 'min': '0', 'max': 63}] expected = {'windspeed': 63} result = self.interface.transmittable_data(weather_data, requested_data) self.assertEqual(expected, result) def test_winddirection(self, mock_class): weather_data = {'winddirection': 'WNW'} expected = {'winddirection': 0x0D} requested_data = [{'key': 'winddirection', 'length': 4}] result = self.interface.transmittable_data(weather_data, requested_data) self.assertEqual(expected, result) def test_windspeed_may_not_exceed_windgusts(self, mock_class): windspeed = 10 weather_data = {'windspeed': windspeed + 1, 'windgusts': windspeed} expected = {'windspeed': windspeed, 'windgusts': windspeed} requested_data = [{ 'key': 'windspeed', 'min': 0, 'step': 1, 'max': 63, 'length': 8 }, { 'key': 'windgusts', 'min': 0, 'step': 1, 'max': 63, 'length': 8 }] result = self.interface.transmittable_data(weather_data, requested_data) self.assertEqual(expected, result)
def setUp(self, mock_class): self.interface = WeatherVaneInterface(**test_config.config)
class WeatherVane(object): def __init__(self, *args, **configuration): self.old_weatherdata = None self.args = args self.configuration = configuration self.interface = WeatherVaneInterface(*args, **configuration) self.display = Display(self.interface, **configuration['display']) logging.info("Using " + str(self.interface)) self.wd = None self.counter = 0 self.interval = configuration['interval'] self.sleep_time = configuration['sleep-time'] self.start_collection_time = datetime.datetime.now() self.end_collection_time = datetime.datetime.now() self.reached = False def start_data_collection(self, pipe_end): """Side effect: reset counter to 0 @param pipe_end_1: @return: """ self.counter = 0 arguments = [pipe_end] arguments.extend(self.args) p = Process(target=fetch_weather_data, args=arguments, kwargs=self.configuration) p.start() logging.debug('Retrieving data') def send_data(self): if self.old_weatherdata: wd = self.interpolate(self.old_weatherdata, self.wd, self.interval) self.interface.send(wd) else: self.interface.send(self.wd) def retrieve_data(self, pipe_end_2): logging.info('Data available:') self.end_collection_time = datetime.datetime.now() self.reached = False logging.info('Data retrieval including parsing took {}'.format( self.end_collection_time - self.start_collection_time)) self.old_weatherdata, self.wd = self.wd, pipe_end_2.recv() logging.info(pprint.pformat(self.wd)) def start_data_collection_and_timer(self, pipe_end_1): self.start_collection_time = datetime.datetime.now() self.start_data_collection(pipe_end_1) def log_heartbeat(self): logging.debug('Heartbeat-{}'.format(self.counter)) def set_logger(self): weathervane_logger = logging.getLogger('') weathervane_logger.setLevel(logging.INFO) handler = logging.handlers.TimedRotatingFileHandler( filename="weathervane.log", when="midnight", interval=1, backupCount=7) formatter = logging.Formatter( "%(asctime)s:%(levelname)s:%(module)s:%(message)s") handler.setFormatter(formatter) weathervane_logger.addHandler(handler) def interpolate(self, old_weatherdata, new_weatherdata, interval): if self.counter >= interval - 1: self.reached = True if new_weatherdata['error']: return new_weatherdata interpolated_wd = {} for key, old_value in list(old_weatherdata.items()): new_value = new_weatherdata.get(key, None) if not new_value: continue if key not in [ 'error', 'winddirection', 'winddirection', 'rain', 'barometric_trend' ] and not self.reached: try: interpolated_value = float(old_value) + ( self.counter * (float(new_value) - float(old_value)) / interval) interpolated_wd[key] = interpolated_value except ValueError: interpolated_wd[key] = new_value except TypeError: interpolated_wd[key] = new_value else: interpolated_wd[key] = new_value return interpolated_wd def main(self): pipe_end_1, pipe_end_2 = Pipe() while True: self.display.tick() if (self.counter % 3) == 0: self.log_heartbeat() if (self.counter % self.interval) == 0: self.start_data_collection_and_timer(pipe_end_1) if pipe_end_2.poll(0): self.retrieve_data(pipe_end_2) if self.wd: self.send_data() self.counter += 1 time.sleep(self.sleep_time)
class WeatherVaneTest(unittest.TestCase): @patch('weathervane.weathervaneinterface.GPIO', autospec=True) def setUp(self, mock_class): self.interface = WeatherVaneInterface(**test_config.config) def noArguments(self, mock_class): self.assertRaises(KeyError, WeatherVaneInterface) def test_init(self, mock_class): result = WeatherVaneInterface(**test_config.config) expected = 'WeatherVaneInterface(channel=0, frequency=250000)' self.assertEqual(expected, str(result), 'Weather Vane Interface failed to be setup correctly - %s, %s' % (expected, result)) def test_bitstring_length(self, mock_class): weather_data = {'wind_direction': 'NO'} self.interface.send(weather_data) self.assertEqual(len(self.interface.new_bit_string), 64) def test_toggle_bit_empty_data(self, mock_class): weather_data = {'wind_direction': 'NO'} self.assertFalse(self.interface.data_changed) self.interface.send(weather_data) self.assertTrue(self.interface.data_changed) def test_bitstring_length_2(self, mock_class): weather_data = {'wind_direction': 'NO', 'wind_speed': 10, 'wind_speed_max': 15, 'wind_speed_bft': 6, 'air_pressure': 1014, 'temperature': 20, 'apparent_temperature': 21, 'humidity': 100} self.interface.send(weather_data) self.assertEqual(len(self.interface.new_bit_string), 64) def test_toggle_bit(self, mock_class): weather_data = {'wind_direction': 'NO', 'wind_speed': 0, 'wind_speed_max': 0, 'wind_speed_bft': 1, 'air_pressure': 900, 'temperature': 20, 'apparent_temperature': 21, 'humidity': 100} self.interface.send(weather_data) self.assertTrue(self.interface.data_changed) weather_data = {'wind_direction': 'O', 'wind_speed': 0, 'wind_speed_max': 0, 'wind_speed_bft': 1, 'air_pressure': 900, 'temperature': 20, 'apparent_temperature': 21, 'humidity': 100} self.interface.send(weather_data) self.assertTrue(self.interface.data_changed) self.interface.send(weather_data) self.assertFalse(self.interface.data_changed) def test_error_wind_direction(self, mock_class): weather_data = {'wind_direction': 'A'} requested_data = {'0': {'key': 'wind_direction', 'length': '4'}} expected = {'wind_direction': 0} result = self.interface.transmittable_data(weather_data, requested_data) self.assertEqual(expected, result) def test_air_pressure(self, mock_class): weather_data = {'air_pressure': 901} requested_data = {'1': {'key': 'air_pressure', 'length': '8', 'max': '1155', 'min': '900', 'step': '1'}} expected = {'air_pressure': 1} result = self.interface.transmittable_data(weather_data, requested_data) self.assertEqual(expected, result) def test_value_rounded(self, mock_class): weather_data = {'air_pressure': 48.493} requested_data = {'0': {'key': 'air_pressure', 'min': '0', 'max': 63}} expected = {'air_pressure': 48} result = self.interface.transmittable_data(weather_data, requested_data) self.assertEqual(expected, result) def test_value_too_low(self, mock_class): weather_data = {'wind_speed': -1} requested_data = {'0': {'key': 'wind_speed', 'min': '0', 'max': 63}} expected = {'wind_speed': 0} result = self.interface.transmittable_data(weather_data, requested_data) self.assertEqual(expected, result) def test_value_too_high(self, mock_class): weather_data = {'wind_speed': 64} requested_data = {'0': {'key': 'wind_speed', 'min': '0', 'max': 63}} expected = {'wind_speed': 63} result = self.interface.transmittable_data(weather_data, requested_data) self.assertEqual(expected, result) def test_wind_direction(self, mock_class): weather_data = {'wind_direction': 'WNW'} expected = {'wind_direction': 0x0D} requested_data = {'0': {'key': 'wind_direction', 'length': 4}} result = self.interface.transmittable_data(weather_data, requested_data) self.assertEqual(expected, result) def test_wind_speed_may_not_exceed_wind_speed_max(self, mock_class): wind_speed = 10 weather_data = {'wind_speed': wind_speed + 1, 'wind_speed_max': wind_speed} expected = {'wind_speed': wind_speed, 'wind_speed_max': wind_speed} requested_data = {'0': {'key': 'wind_speed', 'min': 0, 'step': 1, 'max': 63, 'length': 8}, '1': {'key': 'wind_speed_max', 'min': 0, 'step': 1, 'max': 63, 'length': 8}} result = self.interface.transmittable_data(weather_data, requested_data) self.assertEqual(expected, result)