Esempio n. 1
0
    def _dump_application_info(self):
        pending_migrations = []

        for line in self._intercept_command_stdout('showmigrations',
                                                   no_color=True).split("\n"):
            if line.startswith(' [ ]'):
                pending_migrations.append(line)

        pending_migrations_count = len(pending_migrations)

        self._print_header('DSMR-reader')
        self._pretty_print(
            'App / Python / Database',
            'v{} / v{} / {}'.format(settings.DSMRREADER_VERSION,
                                    platform.python_version(),
                                    connection.vendor))

        self._pretty_print(
            'Backend sleep / Datalogger sleep / Retention cleanup',
            '{}s / {}s / {}h'.format(
                BackendSettings.get_solo().process_sleep,
                DataloggerSettings.get_solo().process_sleep,
                RetentionSettings.get_solo().data_retention_in_hours or '-'))
        self._pretty_print(
            'Telegram latest version read / Parser settings',
            '"{}" / "{}"'.format(MeterStatistics.get_solo().dsmr_version,
                                 DataloggerSettings.get_solo().dsmr_version))

        if pending_migrations_count > 0:
            self._pretty_print('(!) Database migrations pending',
                               '{} (!)'.format(pending_migrations_count))
Esempio n. 2
0
    def test_reading_values(self):
        """ Test whether dsmr_datalogger reads the correct values. """
        DataloggerSettings.get_solo()
        DataloggerSettings.objects.all().update(track_phases=True)

        self._fake_dsmr_reading()
        self.assertTrue(DsmrReading.objects.exists())
        reading = DsmrReading.objects.get()
        self.assertEqual(reading.timestamp,
                         datetime(2016, 3, 3, 15, 43, 47, tzinfo=pytz.UTC))
        self.assertEqual(reading.electricity_delivered_1, Decimal('1073.079'))
        self.assertEqual(reading.electricity_returned_1, Decimal('0'))
        self.assertEqual(reading.electricity_delivered_2, Decimal('1263.199'))
        self.assertEqual(reading.electricity_returned_2, Decimal('0'))
        self.assertEqual(reading.electricity_currently_delivered,
                         Decimal('0.143'))
        self.assertEqual(reading.electricity_currently_returned, Decimal('0'))
        self.assertIsNone(reading.extra_device_timestamp)
        self.assertIsNone(reading.extra_device_delivered)
        self.assertEqual(reading.phase_currently_delivered_l1,
                         Decimal('0.143'))
        self.assertEqual(reading.phase_currently_delivered_l2, None)
        self.assertEqual(reading.phase_currently_delivered_l3, None)

        meter_statistics = MeterStatistics.get_solo()
        self.assertEqual(meter_statistics.electricity_tariff, 2)
        self.assertEqual(meter_statistics.power_failure_count, 6)
        self.assertEqual(meter_statistics.long_power_failure_count, 3)
        self.assertEqual(meter_statistics.voltage_sag_count_l1, 0)
        self.assertEqual(meter_statistics.voltage_sag_count_l2, None)
        self.assertEqual(meter_statistics.voltage_sag_count_l3, None)
        self.assertEqual(meter_statistics.voltage_swell_count_l1, 0)
        self.assertEqual(meter_statistics.voltage_swell_count_l2, None)
        self.assertEqual(meter_statistics.voltage_swell_count_l3, None)
Esempio n. 3
0
    def test_dsmr_fluvius(self):
        DataloggerSettings.get_solo().update(dsmr_version=DataloggerSettings.DSMR_BELGIUM_FLUVIUS)
        self.assertEqual(DataloggerSettings.get_solo().dsmr_version, DataloggerSettings.DSMR_BELGIUM_FLUVIUS)

        connection_parameters = dsmr_datalogger.services.datalogger.get_dsmr_connection_parameters()
        self.assertEqual(connection_parameters['baudrate'], 115200)
        self.assertEqual(connection_parameters['bytesize'], serial.EIGHTBITS)
        self.assertEqual(connection_parameters['parity'], serial.PARITY_NONE)
Esempio n. 4
0
    def test_dsmr_smarty(self):
        DataloggerSettings.get_solo().update(dsmr_version=DataloggerSettings.DSMR_LUXEMBOURG_SMARTY)
        self.assertEqual(DataloggerSettings.get_solo().dsmr_version, DataloggerSettings.DSMR_LUXEMBOURG_SMARTY)

        connection_parameters = dsmr_datalogger.services.datalogger.get_dsmr_connection_parameters()
        self.assertEqual(connection_parameters['baudrate'], 115200)
        self.assertEqual(connection_parameters['bytesize'], serial.EIGHTBITS)
        self.assertEqual(connection_parameters['parity'], serial.PARITY_NONE)
Esempio n. 5
0
    def test_dsmr_version_2(self):
        """ Test connection parameters for DSMR v2. """
        datalogger_settings = DataloggerSettings.get_solo()
        datalogger_settings.dsmr_version = DataloggerSettings.DSMR_VERSION_2
        datalogger_settings.save()

        self.assertEqual(DataloggerSettings.get_solo().dsmr_version, DataloggerSettings.DSMR_VERSION_2)

        connection_parameters = dsmr_datalogger.services.get_dsmr_connection_parameters()
        self.assertEqual(connection_parameters['baudrate'], 9600)
        self.assertEqual(connection_parameters['bytesize'], serial.SEVENBITS)
        self.assertEqual(connection_parameters['parity'], serial.PARITY_EVEN)
    def test_dsmr_version_3(self):
        """ Test connection parameters for DSMR v2/3. """
        datalogger_settings = DataloggerSettings.get_solo()
        datalogger_settings.dsmr_version = DataloggerSettings.DSMR_VERSION_3
        datalogger_settings.save()

        self.assertEqual(DataloggerSettings.get_solo().dsmr_version, DataloggerSettings.DSMR_VERSION_3)

        connection_parameters = dsmr_datalogger.services.get_dsmr_connection_parameters()
        self.assertEqual(connection_parameters['baudrate'], 9600)
        self.assertEqual(connection_parameters['bytesize'], serial.SEVENBITS)
        self.assertEqual(connection_parameters['parity'], serial.PARITY_EVEN)
Esempio n. 7
0
    def test_reading_values(self):
        """ Test whether dsmr_datalogger reads the correct values. """
        DataloggerSettings.get_solo()

        self._fake_dsmr_reading()
        self.assertTrue(DsmrReading.objects.exists())
        reading = DsmrReading.objects.get()
        self.assertEqual(reading.timestamp,
                         datetime(2015, 7, 1, 17, 29, 59, tzinfo=pytz.UTC))
        self.assertEqual(reading.electricity_delivered_1, Decimal('510.747'))
        self.assertEqual(reading.electricity_returned_1, Decimal('0.123'))
        self.assertEqual(reading.electricity_delivered_2, Decimal('500.013'))
        self.assertEqual(reading.electricity_returned_2, Decimal('123.456'))
        self.assertEqual(reading.electricity_currently_delivered,
                         Decimal('0.192'))
        self.assertEqual(reading.electricity_currently_returned,
                         Decimal('0.123'))
        self.assertEqual(reading.extra_device_timestamp,
                         datetime(2015, 7, 1, 17, 0, 0, tzinfo=pytz.UTC))
        self.assertEqual(reading.extra_device_delivered, Decimal('845.206'))
        self.assertEqual(reading.phase_currently_delivered_l1,
                         Decimal('0.123'))
        self.assertEqual(reading.phase_currently_delivered_l2,
                         Decimal('0.456'))
        self.assertEqual(reading.phase_currently_delivered_l3,
                         Decimal('0.789'))
        self.assertEqual(reading.phase_currently_returned_l1, Decimal('0.111'))
        self.assertEqual(reading.phase_currently_returned_l2, Decimal('0.555'))
        self.assertEqual(reading.phase_currently_returned_l3, Decimal('0.999'))
        self.assertIsNone(reading.phase_voltage_l1)
        self.assertIsNone(reading.phase_voltage_l2)
        self.assertIsNone(reading.phase_voltage_l3)
        self.assertEqual(reading.phase_power_current_l1, 0)
        self.assertEqual(reading.phase_power_current_l2, 0)
        self.assertEqual(reading.phase_power_current_l3, 1)

        # Different data source.
        meter_statistics = MeterStatistics.get_solo()
        self.assertEqual(meter_statistics.dsmr_version, '40')
        self.assertEqual(meter_statistics.electricity_tariff, Decimal('1'))
        self.assertEqual(meter_statistics.power_failure_count, 3)
        self.assertEqual(meter_statistics.long_power_failure_count, 0)
        self.assertEqual(meter_statistics.voltage_sag_count_l1, 2)
        self.assertEqual(meter_statistics.voltage_sag_count_l2, 2)
        self.assertEqual(meter_statistics.voltage_sag_count_l3, 0)
        self.assertEqual(meter_statistics.voltage_swell_count_l1, 0)
        self.assertEqual(meter_statistics.voltage_swell_count_l2, 0)
        self.assertEqual(meter_statistics.voltage_swell_count_l3, 0)
Esempio n. 8
0
    def get_context_data(self, **kwargs):
        frontend_settings = FrontendSettings.get_solo()
        weather_settings = WeatherSettings.get_solo()
        context_data = super(Dashboard, self).get_context_data(**kwargs)
        context_data['capabilities'] = dsmr_backend.services.get_capabilities()
        context_data['datalogger_settings'] = DataloggerSettings.get_solo()
        context_data['frontend_settings'] = frontend_settings
        context_data['track_temperature'] = weather_settings.track
        context_data['notifications'] = Notification.objects.unread()

        try:
            latest_electricity = ElectricityConsumption.objects.all().order_by(
                '-read_at')[0]
        except IndexError:
            # Don't even bother when no data available.
            return context_data

        context_data[
            'consumption'] = dsmr_consumption.services.day_consumption(
                day=timezone.localtime(latest_electricity.read_at).date())
        today = timezone.localtime(timezone.now()).date()
        context_data[
            'month_statistics'] = dsmr_stats.services.month_statistics(
                target_date=today)
        return context_data
Esempio n. 9
0
    def get_context_data(self, **kwargs):
        context_data = super(Dashboard, self).get_context_data(**kwargs)
        context_data[
            'capabilities'] = dsmr_backend.services.backend.get_capabilities()
        context_data['datalogger_settings'] = DataloggerSettings.get_solo()
        context_data['frontend_settings'] = FrontendSettings.get_solo()
        context_data['notification_count'] = Notification.objects.unread(
        ).count()
        context_data['django_date_format'] = 'DSMR_GRAPH_LONG_DATE_FORMAT'

        try:
            latest_electricity = ElectricityConsumption.objects.all().order_by(
                '-read_at')[0]
        except IndexError:
            # Don't even bother when no data available.
            return context_data

        context_data[
            'consumption'] = dsmr_consumption.services.day_consumption(
                day=timezone.localtime(latest_electricity.read_at).date())
        month_statistics, days_in_month = dsmr_stats.services.month_statistics(
            target_date=timezone.localtime(timezone.now()).date())
        context_data['month_statistics'] = month_statistics
        context_data['days_in_month'] = days_in_month

        return context_data
Esempio n. 10
0
    def get_context_data(self, **kwargs):
        context_data = super(Statistics, self).get_context_data(**kwargs)
        context_data['capabilities'] = dsmr_backend.services.get_capabilities()
        context_data['electricity_statistics'] = ElectricityStatistics.get_solo().export()

        try:
            latest_reading = DsmrReading.objects.all().order_by('-pk')[0]
        except IndexError:
            pass
        else:
            context_data['latest_reading'] = latest_reading
            context_data['delivered_sum'] = latest_reading.electricity_delivered_1 + \
                latest_reading.electricity_delivered_2
            context_data['returned_sum'] = latest_reading.electricity_returned_1 + \
                latest_reading.electricity_returned_2

        context_data['datalogger_settings'] = DataloggerSettings.get_solo()
        context_data['meter_statistics'] = MeterStatistics.get_solo()

        today = timezone.localtime(timezone.now()).date()

        try:
            context_data['energy_prices'] = EnergySupplierPrice.objects.by_date(today)
        except EnergySupplierPrice.DoesNotExist:
            context_data['energy_prices'] = []

        return context_data
Esempio n. 11
0
    def test_track_phases(self):
        datalogger_settings = DataloggerSettings.get_solo()
        datalogger_settings.track_phases = False
        datalogger_settings.save()

        self.assertFalse(DsmrReading.objects.all().exists())

        dsmr_datalogger.services.telegram_to_reading(data=self.fake_telegram)

        my_reading = DsmrReading.objects.get()
        self.assertIsNone(my_reading.phase_currently_delivered_l1)
        self.assertIsNone(my_reading.phase_currently_delivered_l2)
        self.assertIsNone(my_reading.phase_currently_delivered_l3)

        # Try again, but now with tracking settings enabled.
        DataloggerSettings.objects.all().update(track_phases=True)

        DsmrReading.objects.all().delete()
        dsmr_datalogger.services.telegram_to_reading(data=self.fake_telegram)

        # Should be populated now.
        my_reading = DsmrReading.objects.get()
        self.assertEqual(my_reading.phase_currently_delivered_l1,
                         Decimal('0.123'))
        self.assertEqual(my_reading.phase_currently_delivered_l2,
                         Decimal('0.456'))
        self.assertEqual(my_reading.phase_currently_delivered_l3,
                         Decimal('0.789'))
Esempio n. 12
0
def get_dsmr_connection_parameters():
    """ Returns the communication settings required for the DSMR version set. """
    DSMR_VERSION_MAPPING = {
        DataloggerSettings.DSMR_VERSION_2_3:
        telegram_specifications.V2_2,
        DataloggerSettings.DSMR_VERSION_4_PLUS:
        telegram_specifications.V5,
        DataloggerSettings.DSMR_BELGIUM_FLUVIUS:
        telegram_specifications.BELGIUM_FLUVIUS,
        DataloggerSettings.DSMR_LUXEMBOURG_SMARTY:
        telegram_specifications.LUXEMBOURG_SMARTY,
    }

    datalogger_settings = DataloggerSettings.get_solo()
    connection_parameters = {
        'com_port': datalogger_settings.com_port,
        'baudrate': 115200,
        'bytesize': serial.EIGHTBITS,
        'parity': serial.PARITY_NONE,
    }

    if datalogger_settings.dsmr_version == DataloggerSettings.DSMR_VERSION_2_3:
        connection_parameters.update({
            'baudrate': 9600,
            'bytesize': serial.SEVENBITS,
            'parity': serial.PARITY_EVEN,
        })

    connection_parameters.update(
        dict(specifications=DSMR_VERSION_MAPPING[
            datalogger_settings.dsmr_version]))
    return connection_parameters
    def test_reading_values(self):
        DataloggerSettings.get_solo()

        self._fake_dsmr_reading()
        self.assertTrue(DsmrReading.objects.exists())
        reading = DsmrReading.objects.get()
        self.assertEqual(reading.timestamp,
                         datetime(2010, 12, 9, 10, 30, 20, tzinfo=pytz.UTC))
        self.assertEqual(reading.electricity_delivered_1,
                         Decimal('123456.789'))
        self.assertEqual(reading.electricity_returned_1, Decimal('123456.789'))
        self.assertEqual(reading.electricity_delivered_2,
                         Decimal('123456.789'))
        self.assertEqual(reading.electricity_returned_2, Decimal('123456.789'))
        self.assertEqual(reading.electricity_currently_delivered,
                         Decimal('1.193'))
        self.assertEqual(reading.electricity_currently_returned, Decimal('0'))
        self.assertEqual(reading.extra_device_timestamp,
                         datetime(2010, 12, 9, 10, 25, tzinfo=pytz.UTC))
        self.assertEqual(reading.extra_device_delivered, Decimal('12785.123'))
        self.assertEqual(reading.phase_currently_delivered_l1,
                         Decimal('1.111'))
        self.assertEqual(reading.phase_currently_delivered_l2,
                         Decimal('2.222'))
        self.assertEqual(reading.phase_currently_delivered_l3,
                         Decimal('3.333'))
        self.assertEqual(reading.phase_currently_returned_l1, Decimal('4.444'))
        self.assertEqual(reading.phase_currently_returned_l2, Decimal('5.555'))
        self.assertEqual(reading.phase_currently_returned_l3, Decimal('6.666'))
        self.assertEqual(reading.phase_voltage_l1, Decimal('220.1'))
        self.assertEqual(reading.phase_voltage_l2, Decimal('220.2'))
        self.assertEqual(reading.phase_voltage_l3, Decimal('220.3'))
        self.assertEqual(reading.phase_power_current_l1, 1)
        self.assertEqual(reading.phase_power_current_l2, 2)
        self.assertEqual(reading.phase_power_current_l3, 3)

        meter_statistics = MeterStatistics.get_solo()
        self.assertEqual(meter_statistics.dsmr_version, '50')
        self.assertEqual(meter_statistics.electricity_tariff, 2)
        self.assertEqual(meter_statistics.power_failure_count, 4)
        self.assertEqual(meter_statistics.long_power_failure_count, 2)
        self.assertEqual(meter_statistics.voltage_sag_count_l1, 2)
        self.assertEqual(meter_statistics.voltage_sag_count_l2, 1)
        self.assertEqual(meter_statistics.voltage_sag_count_l3, 0)
        self.assertEqual(meter_statistics.voltage_swell_count_l1, 0)
        self.assertEqual(meter_statistics.voltage_swell_count_l2, 3)
        self.assertEqual(meter_statistics.voltage_swell_count_l3, 0)
Esempio n. 14
0
    def test_dashboard_xhr_graphs(self, now_mock):
        now_mock.return_value = timezone.make_aware(
            timezone.datetime(2015, 11, 15))

        if self.support_data:
            weather_settings = WeatherSettings.get_solo()
            weather_settings.track = True
            weather_settings.save()

            # Test with phases feature switched on as well.
            DataloggerSettings.get_solo()
            DataloggerSettings.objects.update(track_phases=True)

        # Send seperate offset as well.
        response = self.client.get(reverse('{}:dashboard-xhr-graphs'.format(
            self.namespace)),
                                   data={
                                       'electricity_offset': 24,
                                       'gas_offset': 10
                                   })
        self.assertEqual(response.status_code, 200, response.content)

        # Send invalid offset.
        response = self.client.get(reverse('{}:dashboard-xhr-graphs'.format(
            self.namespace)),
                                   data={
                                       'electricity_offset': 'abc',
                                       'gas_offset': 'xzy'
                                   })
        self.assertEqual(response.status_code, 400)

        response = self.client.get(
            reverse('{}:dashboard-xhr-graphs'.format(self.namespace)))
        self.assertEqual(response.status_code, 200, response.content)
        json_content = json.loads(response.content.decode("utf8"))

        if self.support_data:
            self.assertGreater(len(json_content['electricity_x']), 0)
            self.assertGreater(len(json_content['electricity_y']), 0)

            self.assertIn('phases_l1_y', json_content)
            self.assertIn('phases_l2_y', json_content)
            self.assertIn('phases_l3_y', json_content)

        if self.support_gas:
            self.assertGreater(len(json_content['gas_x']), 0)
            self.assertGreater(len(json_content['gas_y']), 0)
Esempio n. 15
0
    def test_dsmr_version_4(self):
        """ Test connection parameters for DSMR v4. """
        self.assertEqual(DataloggerSettings.get_solo().dsmr_version, DataloggerSettings.DSMR_VERSION_4)

        connection_parameters = dsmr_datalogger.services.get_dsmr_connection_parameters()
        self.assertEqual(connection_parameters['baudrate'], 115200)
        self.assertEqual(connection_parameters['bytesize'], serial.EIGHTBITS)
        self.assertEqual(connection_parameters['parity'], serial.PARITY_NONE)
Esempio n. 16
0
    def _check_restart_required(self):
        if not DataloggerSettings.get_solo().restart_required:
            return

        DataloggerSettings.objects.update(restart_required=False)
        logger.warning(
            'Detected datalogger restart required, stopping process...')
        raise StopInfiniteRun()
Esempio n. 17
0
    def test_dsmr_version_4_plus(self):
        """ Test connection parameters for DSMR v4+. """
        self.assertEqual(DataloggerSettings.get_solo().dsmr_version, DataloggerSettings.DSMR_VERSION_4_PLUS)

        connection_parameters = dsmr_datalogger.services.get_dsmr_connection_parameters()
        self.assertEqual(connection_parameters['baudrate'], 115200)
        self.assertEqual(connection_parameters['bytesize'], serial.EIGHTBITS)
        self.assertEqual(connection_parameters['parity'], serial.PARITY_NONE)
Esempio n. 18
0
    def test_reading_values(self):
        """ Test whether dsmr_datalogger reads the correct values. """
        DataloggerSettings.get_solo()

        self._fake_dsmr_reading()
        self.assertTrue(DsmrReading.objects.exists())
        reading = DsmrReading.objects.get()
        self.assertEqual(reading.timestamp,
                         datetime(2016, 3, 17, 21, 10, 58, tzinfo=pytz.UTC))
        self.assertEqual(reading.electricity_delivered_1, Decimal('1255.252'))
        self.assertEqual(reading.electricity_returned_1, Decimal('0'))
        self.assertEqual(reading.electricity_delivered_2, Decimal('1284.838'))
        self.assertEqual(reading.electricity_returned_2, Decimal('0'))
        self.assertEqual(reading.electricity_currently_delivered,
                         Decimal('0.187'))
        self.assertEqual(reading.electricity_currently_returned, Decimal('0'))
        self.assertEqual(reading.extra_device_timestamp,
                         datetime(2016, 3, 17, 21, 0, 0, tzinfo=pytz.UTC))
        self.assertEqual(reading.extra_device_delivered, Decimal('1438.997'))
        self.assertEqual(reading.phase_currently_delivered_l1,
                         Decimal('0.187'))
        self.assertIsNone(reading.phase_currently_delivered_l2)
        self.assertIsNone(reading.phase_currently_delivered_l3)
        self.assertEqual(reading.phase_currently_returned_l1, Decimal('0.999'))
        self.assertIsNone(reading.phase_currently_returned_l2)
        self.assertIsNone(reading.phase_currently_returned_l3)
        self.assertIsNone(reading.phase_voltage_l1)
        self.assertIsNone(reading.phase_voltage_l2)
        self.assertIsNone(reading.phase_voltage_l3)
        self.assertEqual(reading.phase_power_current_l1, 1)
        self.assertEqual(reading.phase_power_current_l2, None)
        self.assertEqual(reading.phase_power_current_l3, None)

        # Different data source.
        meter_statistics = MeterStatistics.get_solo()
        self.assertEqual(meter_statistics.dsmr_version, '42')
        self.assertEqual(meter_statistics.electricity_tariff, 2)
        self.assertEqual(meter_statistics.power_failure_count, 8)
        self.assertEqual(meter_statistics.long_power_failure_count, 0)
        self.assertEqual(meter_statistics.voltage_sag_count_l1, 0)
        self.assertIsNone(meter_statistics.voltage_sag_count_l2)
        self.assertIsNone(meter_statistics.voltage_sag_count_l3)
        self.assertEqual(meter_statistics.voltage_swell_count_l1, 0)
        self.assertIsNone(meter_statistics.voltage_swell_count_l2)
        self.assertIsNone(meter_statistics.voltage_swell_count_l3)
Esempio n. 19
0
    def test_tracking_disabled(self):
        """ Test whether datalogger can bij stopped by changing track setting. """
        datalogger_settings = DataloggerSettings.get_solo()
        datalogger_settings.track = False
        datalogger_settings.save()

        # Datalogger should crash with error.
        with self.assertRaisesMessage(CommandError, 'Datalogger tracking is DISABLED!'):
            self._intercept_command_stdout('dsmr_datalogger')
Esempio n. 20
0
    def test_tracking_disabled(self):
        """ Test whether datalogger can bij stopped by changing track setting. """
        datalogger_settings = DataloggerSettings.get_solo()
        datalogger_settings.track = False
        datalogger_settings.save()

        # Datalogger should crash with error.
        with self.assertRaisesMessage(CommandError, 'Datalogger tracking is DISABLED!'):
            self._intercept_command_stdout('dsmr_datalogger', run_once=True)
    def test_reading_values(self):
        """ Test whether dsmr_datalogger reads the correct values. """
        DataloggerSettings.get_solo()

        self._fake_dsmr_reading()
        self.assertTrue(DsmrReading.objects.exists())
        reading = DsmrReading.objects.get()
        self.assertEqual(reading.timestamp,
                         datetime(2016, 2, 10, 19, 30, 34, tzinfo=pytz.UTC))
        self.assertEqual(reading.electricity_delivered_1, Decimal('756.849'))
        self.assertEqual(reading.electricity_returned_1, Decimal('0'))
        self.assertEqual(reading.electricity_delivered_2, Decimal('714.405'))
        self.assertEqual(reading.electricity_returned_2, Decimal('0'))
        self.assertEqual(reading.electricity_currently_delivered,
                         Decimal('0.111'))
        self.assertEqual(reading.electricity_currently_returned, Decimal('0'))
        self.assertEqual(reading.extra_device_timestamp,
                         datetime(2016, 2, 10, 19, 0, 0, tzinfo=pytz.UTC))
        self.assertEqual(reading.extra_device_delivered, Decimal('1197.484'))
        self.assertEqual(reading.phase_currently_delivered_l1,
                         Decimal('0.123'))
        self.assertEqual(reading.phase_currently_delivered_l2,
                         Decimal('0.456'))
        self.assertEqual(reading.phase_currently_delivered_l3,
                         Decimal('0.789'))
        self.assertEqual(reading.phase_currently_returned_l1, Decimal('0.222'))
        self.assertEqual(reading.phase_currently_returned_l2, Decimal('0.444'))
        self.assertEqual(reading.phase_currently_returned_l3, Decimal('0.666'))
        self.assertIsNone(reading.phase_voltage_l1)
        self.assertIsNone(reading.phase_voltage_l2)
        self.assertIsNone(reading.phase_voltage_l3)

        # Different data source.
        meter_statistics = MeterStatistics.get_solo()
        self.assertEqual(meter_statistics.dsmr_version, '42')
        self.assertEqual(meter_statistics.electricity_tariff, 2)
        self.assertEqual(meter_statistics.power_failure_count, 3)
        self.assertEqual(meter_statistics.long_power_failure_count, 0)
        self.assertEqual(meter_statistics.voltage_sag_count_l1, 2)
        self.assertEqual(meter_statistics.voltage_sag_count_l2, 2)
        self.assertEqual(meter_statistics.voltage_sag_count_l3, 0)
        self.assertEqual(meter_statistics.voltage_swell_count_l1, 0)
        self.assertEqual(meter_statistics.voltage_swell_count_l2, 0)
        self.assertEqual(meter_statistics.voltage_swell_count_l3, 0)
Esempio n. 22
0
 def get_context_data(self, **kwargs):
     context_data = super(Configuration, self).get_context_data(**kwargs)
     context_data['api_settings'] = APISettings.get_solo()
     context_data['consumption_settings'] = ConsumptionSettings.get_solo()
     context_data['datalogger_settings'] = DataloggerSettings.get_solo()
     context_data['frontend_settings'] = FrontendSettings.get_solo()
     context_data['weather_settings'] = WeatherSettings.get_solo()
     context_data['backup_settings'] = BackupSettings.get_solo()
     context_data['dropbox_settings'] = DropboxSettings.get_solo()
     context_data['mindergas_settings'] = MinderGasSettings.get_solo()
     return context_data
Esempio n. 23
0
    def get_context_data(self, **kwargs):
        context_data = super(Dashboard, self).get_context_data(**kwargs)
        context_data['capabilities'] = dsmr_backend.services.get_capabilities()
        context_data['datalogger_settings'] = DataloggerSettings.get_solo()
        context_data['frontend_settings'] = FrontendSettings.get_solo()
        context_data['notifications'] = Notification.objects.unread()

        today = timezone.localtime(timezone.now()).date()
        context_data[
            'month_statistics'] = dsmr_stats.services.month_statistics(
                target_date=today)
        return context_data
Esempio n. 24
0
    def _reading_with_override_telegram_timestamp_active(self, now_mock) -> DsmrReading:
        """ For DRY. Returns the reading. """
        now_mock.return_value = timezone.make_aware(timezone.datetime(2021, 1, 15, 12, 34, 56, 0))

        datalogger_settings = DataloggerSettings.get_solo()
        datalogger_settings.update(override_telegram_timestamp=True)

        self._fake_dsmr_reading()
        self.assertTrue(DsmrReading.objects.exists())

        reading = DsmrReading.objects.get()
        self.assertEqual(reading.timestamp, datetime(2021, 1, 15, 11, 34, 56, 0, tzinfo=pytz.UTC))  # CET > UTC

        return reading
Esempio n. 25
0
    def get_context_data(self, **kwargs):
        context_data = super(Dashboard, self).get_context_data(**kwargs)
        context_data[
            'capabilities'] = dsmr_backend.services.backend.get_capabilities()
        context_data['datalogger_settings'] = DataloggerSettings.get_solo()
        context_data['frontend_settings'] = FrontendSettings.get_solo()
        context_data['notification_count'] = Notification.objects.unread(
        ).count()
        context_data['today_date_format'] = 'DSMR_DATEPICKER_DAY'
        context_data['month_date_format'] = 'DSMR_DATEPICKER_MONTH'
        context_data['year_date_format'] = 'DSMR_DATEPICKER_YEAR'
        context_data['period_totals'] = dsmr_stats.services.period_totals()

        return context_data
Esempio n. 26
0
    def run(self, **options):
        """ InfiniteManagementCommandMixin listens to handle() and calls run() in a loop. """
        datalogger_settings = DataloggerSettings.get_solo()

        # This should only by disabled when performing huge migrations.
        if not datalogger_settings.track:
            raise CommandError("Datalogger tracking is DISABLED!")

        telegram = dsmr_datalogger.services.read_telegram()

        # Reflect output to STDOUT for logging and convenience.
        self.stdout.write(telegram)

        dsmr_datalogger.services.telegram_to_reading(data=telegram)
Esempio n. 27
0
    def test_okay(self, serial_readline_mock, serial_open_mock):
        """ Fake & process an DSMR vX telegram reading. """
        serial_open_mock.return_value = None
        serial_readline_mock.side_effect = self._dsmr_dummy_data()

        # The big difference here is that CRC verification should be off.
        datalogger_settings = DataloggerSettings.get_solo()
        datalogger_settings.verify_telegram_crc = False
        datalogger_settings.save()

        self.assertFalse(DsmrReading.objects.exists())

        self._intercept_command_stdout('dsmr_datalogger')

        self.assertTrue(DsmrReading.objects.exists())
Esempio n. 28
0
    def test_discard_telegram_with_future_timestamp(self, serial_readline_mock, serial_open_mock, now_mock):
        """ Telegrams with timestamps in the (far) future should be rejected. """
        serial_open_mock.return_value = None
        serial_readline_mock.side_effect = self._dsmr_dummy_data()
        now_mock.return_value = timezone.make_aware(timezone.datetime(2017, 1, 1, hour=9, minute=0, second=0))

        datalogger_settings = DataloggerSettings.get_solo()
        datalogger_settings.verify_telegram_crc = False  # Not important for this test.
        datalogger_settings.save()

        self.assertFalse(DsmrReading.objects.exists())
        self._intercept_command_stdout('dsmr_datalogger', run_once=True)

        # It should be discarded.
        self.assertFalse(DsmrReading.objects.exists())
Esempio n. 29
0
    def run(self, **options):
        """ InfiniteManagementCommandMixin listens to handle() and calls run() in a loop. """
        datalogger_settings = DataloggerSettings.get_solo()

        # This should only be disabled when performing huge migrations.
        if not datalogger_settings.track:
            raise CommandError("Datalogger tracking is DISABLED!")

        telegram = dsmr_datalogger.services.read_telegram()
        logger.info("\n%s", telegram)

        try:
            dsmr_datalogger.services.telegram_to_reading(data=telegram)
        except InvalidTelegramError:
            # The service called already logs the error.
            pass
Esempio n. 30
0
    def get_context_data(self, **kwargs):
        context_data = super(LiveGraphs, self).get_context_data(**kwargs)
        context_data[
            'capabilities'] = dsmr_backend.services.backend.get_capabilities()
        context_data['meter_statistics'] = MeterStatistics.get_solo()
        context_data['datalogger_settings'] = DataloggerSettings.get_solo()
        context_data['frontend_settings'] = FrontendSettings.get_solo()
        context_data['notification_count'] = Notification.objects.unread(
        ).count()
        context_data['sorted_graphs_json'] = json.dumps(
            list(SortedGraph.objects.all().values_list('graph_type',
                                                       flat=True)))

        today = timezone.localtime(timezone.now()).date()
        context_data[
            'month_statistics'] = dsmr_stats.services.month_statistics(
                target_date=today)
        return context_data
Esempio n. 31
0
    def get_context_data(self, **kwargs):
        context_data = super(Statistics, self).get_context_data(**kwargs)
        context_data['capabilities'] = dsmr_backend.services.get_capabilities()

        try:
            latest_reading = DsmrReading.objects.all().order_by('-pk')[0]
        except IndexError:
            pass
        else:
            context_data['latest_reading'] = latest_reading
            context_data['delivered_sum'] = latest_reading.electricity_delivered_1 + \
                latest_reading.electricity_delivered_2
            context_data['returned_sum'] = latest_reading.electricity_returned_1 + \
                latest_reading.electricity_returned_2

        context_data['datalogger_settings'] = DataloggerSettings.get_solo()
        context_data['meter_statistics'] = MeterStatistics.get_solo()

        return context_data
Esempio n. 32
0
def get_dsmr_connection_parameters():
    """ Returns the communication settings required for the DSMR version set. """
    DSMR_VERSION_MAPPING = {
        DataloggerSettings.DSMR_VERSION_3: {
            'baudrate': 9600,
            'bytesize': serial.SEVENBITS,
            'parity': serial.PARITY_EVEN,
        },
        DataloggerSettings.DSMR_VERSION_4: {
            'baudrate': 115200,
            'bytesize': serial.EIGHTBITS,
            'parity': serial.PARITY_NONE,
        },
    }

    datalogger_settings = DataloggerSettings.get_solo()
    connection_parameters = DSMR_VERSION_MAPPING[datalogger_settings.dsmr_version]
    connection_parameters['com_port'] = datalogger_settings.com_port
    return connection_parameters
Esempio n. 33
0
def get_dsmr_connection_parameters() -> Dict:
    """ Returns the communication settings required for the DSMR version set. """
    DSMR_VERSION_MAPPING = {
        DataloggerSettings.DSMR_VERSION_2_3:
        telegram_specifications.V2_2,
        DataloggerSettings.DSMR_VERSION_4_PLUS:
        telegram_specifications.V5,
        DataloggerSettings.DSMR_BELGIUM_FLUVIUS:
        telegram_specifications.BELGIUM_FLUVIUS,
        DataloggerSettings.DSMR_LUXEMBOURG_SMARTY:
        telegram_specifications.LUXEMBOURG_SMARTY,
    }

    datalogger_settings = DataloggerSettings.get_solo()
    is_default_dsmr_protocol = datalogger_settings.dsmr_version != DataloggerSettings.DSMR_VERSION_2_3
    connection_parameters = dict(
        telegram_timeout=
        20,  # After this threshold, the datalogger will throw an exception to break the infinite loop.
        specifications=DSMR_VERSION_MAPPING[datalogger_settings.dsmr_version],
    )

    extra_connection_parameters = {
        DataloggerSettings.INPUT_METHOD_SERIAL:
        dict(
            url_or_port=datalogger_settings.serial_port,
            baudrate=115200 if is_default_dsmr_protocol else 9600,
            bytesize=serial.EIGHTBITS
            if is_default_dsmr_protocol else serial.SEVENBITS,
            parity=serial.PARITY_NONE
            if is_default_dsmr_protocol else serial.PARITY_EVEN,
            stopbits=serial.STOPBITS_ONE,
            xonxoff=1,
            rtscts=0,
        ),
        DataloggerSettings.INPUT_METHOD_IPV4:
        dict(url_or_port='socket://{}:{}'.format(
            datalogger_settings.network_socket_address,
            datalogger_settings.network_socket_port))
    }[datalogger_settings.input_method]

    connection_parameters.update(extra_connection_parameters)
    return connection_parameters
Esempio n. 34
0
 def get_context_data(self, **kwargs):
     context_data = super(Configuration, self).get_context_data(**kwargs)
     # 20+ queries, we should cache this at some point.
     context_data.update(
         dict(
             api_settings=APISettings.get_solo(),
             backend_settings=BackendSettings.get_solo(),
             backup_settings=BackupSettings.get_solo(),
             consumption_settings=ConsumptionSettings.get_solo(),
             datalogger_settings=DataloggerSettings.get_solo(),
             dropbox_settings=DropboxSettings.get_solo(),
             email_settings=EmailSettings.get_solo(),
             frontend_settings=FrontendSettings.get_solo(),
             mindergas_settings=MinderGasSettings.get_solo(),
             mqtt_broker_settings=MQTTBrokerSettings.get_solo(),
             mqtt_jsondaytotals_settings=JSONDayTotalsMQTTSettings.get_solo(
             ),
             mqtt_splittopicdaytotals_settings=
             SplitTopicDayTotalsMQTTSettings.get_solo(),
             mqtt_jsoncurrentperiodtotals_settings=
             JSONCurrentPeriodTotalsMQTTSettings.get_solo(),
             mqtt_splittopiccurrentperiodtotals_settings=
             SplitTopicCurrentPeriodTotalsMQTTSettings.get_solo(),
             mqtt_jsongasconsumption_settings=JSONGasConsumptionMQTTSettings
             .get_solo(),
             mqtt_splittopicgasconsumption_settings=
             SplitTopicGasConsumptionMQTTSettings.get_solo(),
             mqtt_splittopicmeterstatistics_settings=
             SplitTopicMeterStatisticsMQTTSettings.get_solo(),
             mqtt_jsontelegram_settings=JSONTelegramMQTTSettings.get_solo(),
             mqtt_rawtelegram_settings=RawTelegramMQTTSettings.get_solo(),
             mqtt_splittopictelegram_settings=SplitTopicTelegramMQTTSettings
             .get_solo(),
             notification_settings=NotificationSetting.get_solo(),
             pvoutput_api_settings=PVOutputAPISettings.get_solo(),
             pvoutput_addstatus_settings=PVOutputAddStatusSettings.get_solo(
             ),
             retention_settings=RetentionSettings.get_solo(),
             weather_settings=WeatherSettings.get_solo(),
             influxdb_settings=InfluxdbIntegrationSettings.get_solo(),
         ))
     return context_data
Esempio n. 35
0
def get_dsmr_connection_parameters():
    """ Returns the communication settings required for the DSMR version set. """
    DSMR_VERSION_MAPPING = {
        DataloggerSettings.DSMR_VERSION_2: {
            'baudrate': 9600,
            'bytesize': serial.SEVENBITS,
            'parity': serial.PARITY_EVEN,
            'crc': False,
        },
        DataloggerSettings.DSMR_VERSION_4_PLUS: {
            'baudrate': 115200,
            'bytesize': serial.EIGHTBITS,
            'parity': serial.PARITY_NONE,
            'crc': True,
        },
    }

    datalogger_settings = DataloggerSettings.get_solo()
    connection_parameters = DSMR_VERSION_MAPPING[datalogger_settings.dsmr_version]
    connection_parameters['com_port'] = datalogger_settings.com_port
    return connection_parameters
Esempio n. 36
0
    def get_context_data(self, **kwargs):
        context_data = super(Statistics, self).get_context_data(**kwargs)
        context_data['capabilities'] = dsmr_backend.services.get_capabilities()

        try:
            context_data['latest_reading'] = DsmrReading.objects.all().order_by('-pk')[0]
        except IndexError:
            pass

        today = timezone.localtime(timezone.now()).date()
        context_data['datalogger_settings'] = DataloggerSettings.get_solo()
        context_data['meter_statistics'] = MeterStatistics.get_solo()

        try:
            context_data['energy_prices'] = EnergySupplierPrice.objects.by_date(today)
        except EnergySupplierPrice.DoesNotExist:
            pass

        # Use stats
        context_data['slumber_consumption_watt'] = dsmr_consumption.services.calculate_slumber_consumption_watt()
        context_data['min_max_consumption_watt'] = dsmr_consumption.services.calculate_min_max_consumption_watt()

        return context_data
Esempio n. 37
0
    def test_track_meter_statistics(self):
        datalogger_settings = DataloggerSettings.get_solo()
        datalogger_settings.track_meter_statistics = False
        datalogger_settings.save()

        fake_telegram = ''.join([
            "/XMX5LGBBFFB123456789\r\n",
            "\r\n",
            "1-3:0.2.8(40)\r\n",
            "0-0:1.0.0(151110192959W)\r\n",
            "0-0:96.1.1(xxxxxxxxxxxxx)\r\n",
            "1-0:1.8.1(000510.747*kWh)\r\n",
            "1-0:2.8.1(000000.123*kWh)\r\n",
            "1-0:1.8.2(000500.013*kWh)\r\n",
            "1-0:2.8.2(000123.456*kWh)\r\n",
            "0-0:96.14.0(0001)\r\n",
            "1-0:1.7.0(00.192*kW)\r\n",
            "1-0:2.7.0(00.123*kW)\r\n",
            "0-0:17.0.0(999.9*kW)\r\n",
            "0-0:96.3.10(1)\r\n",
            "0-0:96.7.21(00003)\r\n",
            "0-0:96.7.9(00000)\r\n",
            "1-0:99.97.0(0)(0-0:96.7.19)\r\n",
            "1-0:32.32.0(00002)\r\n",
            "1-0:52.32.0(00002)\r\n",
            "1-0:72.32.0(00000)\r\n",
            "1-0:32.36.0(00000)\r\n",
            "1-0:52.36.0(00000)\r\n",
            "1-0:72.36.0(00000)\r\n",
            "0-0:96.13.1()\r\n",
            "0-0:96.13.0()\r\n",
            "1-0:31.7.0(000*A)\r\n",
            "1-0:51.7.0(000*A)\r\n",
            "1-0:71.7.0(001*A)\r\n",
            "1-0:21.7.0(00.000*kW)\r\n",
            "1-0:41.7.0(00.000*kW)\r\n",
            "1-0:61.7.0(00.192*kW)\r\n",
            "1-0:22.7.0(00.000*kW)\r\n",
            "1-0:42.7.0(00.000*kW)\r\n",
            "1-0:62.7.0(00.000*kW)\r\n",
            "0-1:24.1.0(003)\r\n",
            "0-1:96.1.0(xxxxxxxxxxxxx)\r\n",
            "0-1:24.2.1(151110190000W)(00845.206*m3)\r\n",
            "0-1:24.4.0(1)\r\n",
            "!D19A\n",
        ])

        self.assertIsNone(MeterStatistics.get_solo().electricity_tariff)  # Empty model in DB.
        dsmr_datalogger.services.telegram_to_reading(data=fake_telegram)
        self.assertIsNone(MeterStatistics.get_solo().electricity_tariff)  # Unaffected

        # Try again, but now with tracking settings enabled.
        datalogger_settings = DataloggerSettings.get_solo()
        datalogger_settings.track_meter_statistics = True
        datalogger_settings.save()

        self.assertIsNone(MeterStatistics.get_solo().electricity_tariff)  # Empty model in DB.
        dsmr_datalogger.services.telegram_to_reading(data=fake_telegram)

        # Should be populated now.
        meter_statistics = MeterStatistics.get_solo()
        self.assertIsNotNone(meter_statistics.electricity_tariff)
        self.assertEqual(meter_statistics.electricity_tariff, 1)
        self.assertEqual(meter_statistics.power_failure_count, 3)
        self.assertEqual(meter_statistics.voltage_sag_count_l1, 2)
        self.assertEqual(meter_statistics.voltage_sag_count_l2, 2)
Esempio n. 38
0
def telegram_to_reading(data):
    """
    Converts a P1 telegram to a DSMR reading, which will be stored in database.
    """

    def _get_reading_fields():
        reading_fields = DsmrReading._meta.get_all_field_names()
        reading_fields.remove('id')
        reading_fields.remove('processed')
        return reading_fields

    def _get_statistics_fields():
        reading_fields = MeterStatistics._meta.get_all_field_names()
        reading_fields.remove('id')
        return reading_fields

    def _convert_legacy_dsmr_gas_line(parsed_reading, current_line, next_line):
        """ Legacy support for DSMR 2.x gas. """
        legacy_gas_line = current_line

        if next_line.startswith('('):
            legacy_gas_line = current_line + next_line

        legacy_gas_result = re.search(
            r'[^(]+\((\d+)\)\(\d+\)\(\d+\)\(\d+\)\([0-9-.:]+\)\(m3\)\(([0-9.]+)\)',
            legacy_gas_line
        )
        gas_timestamp = legacy_gas_result.group(1)

        if timezone.now().dst() != timezone.timedelta(0):
            gas_timestamp += 'S'
        else:
            gas_timestamp += 'W'

        parsed_reading['extra_device_timestamp'] = reading_timestamp_to_datetime(
            string=gas_timestamp
        )
        parsed_reading['extra_device_delivered'] = legacy_gas_result.group(2)
        return parsed_reading

    # Defaults all fields to NULL.
    parsed_reading = {k: None for k in _get_reading_fields() + _get_statistics_fields()}
    field_splitter = re.compile(r'([^(]+)\((.+)\)')
    lines_read = data.split("\r\n")

    for index, current_line in enumerate(lines_read):
        result = field_splitter.search(current_line)

        if not result:
            continue

        code = result.group(1)

        # M-bus (0-n:24.1) cannot identify the type of device, see issue #92.
        if code in ('0-2:24.2.1', '0-3:24.2.1', '0-4:24.2.1'):
            code = '0-1:24.2.1'

        # DSMR 2.x emits gas readings in different format.
        if code == '0-1:24.3.0':
            parsed_reading = _convert_legacy_dsmr_gas_line(
                parsed_reading, current_line, lines_read[index + 1]
            )
            continue

        try:
            field = DSMR_MAPPING[code]
        except KeyError:
            continue

        value = result.group(2)

        # Drop units, as the database does not care for them.
        value = value.replace('*kWh', '').replace('*kW', '').replace('*m3', '')

        # Extra device parameters are placed on a single line, meh.
        if code == "0-1:24.2.1":
            timestamp_value, gas_usage = value.split(")(")
            parsed_reading[field[0]] = reading_timestamp_to_datetime(string=timestamp_value)
            parsed_reading[field[1]] = gas_usage
        else:
            if field == "timestamp":
                value = reading_timestamp_to_datetime(string=value)

            parsed_reading[field] = value

    # Hack for DSMR 2.x legacy, which lacks timestamp info..
    if parsed_reading['timestamp'] is None:
        parsed_reading['timestamp'] = timezone.now()

    # Now we need to split reading & statistics. So we split the dict here.
    reading_kwargs = {k: parsed_reading[k] for k in _get_reading_fields()}
    new_reading = DsmrReading.objects.create(**reading_kwargs)

    # Optional feature.
    if DataloggerSettings.get_solo().track_meter_statistics:
        statistics_kwargs = {k: parsed_reading[k] for k in _get_statistics_fields()}
        # There should already be one in database, created when migrating.
        MeterStatistics.objects.all().update(**statistics_kwargs)

    return new_reading