Ejemplo n.º 1
0
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)
Ejemplo n.º 2
0
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)
Ejemplo n.º 3
0
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)
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)