Пример #1
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

        return context_data
Пример #2
0
def _compact_gas(dsmr_reading, grouping_type, **kwargs):
    """
    Compacts any DSMR readings to gas consumption records, optionally grouped. Only when there is support for gas.

    There is quite some distinction between DSMR v4 and v5. DSMR v4 will update only once per hour and backtracks the
    time by reporting it over the previous hour.
    DSMR v5 will just allow small intervals, depending on whether the readings are grouped per minute or not.
    """
    if not dsmr_reading.extra_device_timestamp or not dsmr_reading.extra_device_delivered:
        # Some households aren't connected to a gas meter at all.
        return

    read_at = dsmr_reading.extra_device_timestamp
    dsmr_version = MeterStatistics.get_solo().dsmr_version

    # User requests grouping? We will truncate the 'seconds' marker, which will only affect DSMR v5 readings.
    if grouping_type == ConsumptionSettings.COMPACTOR_GROUPING_BY_MINUTE:
        read_at = read_at.replace(second=0, microsecond=0)

    # DSMR v4 readings should reflect to the previous hour, to keep it compatible with the existing implementation.
    if dsmr_version is not None and dsmr_version.startswith('4'):
        read_at = read_at - timezone.timedelta(hours=1)

    # We will not override data, just ignore it then. DSMR v4 will hit this a lot, DSMR v5 not.
    if GasConsumption.objects.filter(read_at=read_at).exists():
        return

    # DSMR does not expose current gas rate, so we have to calculate it ourselves, relative to the previous gas
    # consumption, if any.
    try:
        previous = GasConsumption.objects.all().order_by('-read_at')[0]
    except IndexError:
        gas_diff = 0
    else:
        gas_diff = dsmr_reading.extra_device_delivered - previous.delivered

    GasConsumption.objects.create(
        read_at=read_at,
        delivered=dsmr_reading.extra_device_delivered,
        currently_delivered=gas_diff)
Пример #3
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(2015, 11, 10, 18, 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, 11, 10, 18, 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'))

        # 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)
Пример #4
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, 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)

        # 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)
Пример #5
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, 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.assertIsNone(reading.phase_currently_delivered_l2)
        self.assertIsNone(reading.phase_currently_delivered_l3)
        self.assertEqual(reading.phase_currently_returned_l1, Decimal('0.321'))
        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, 0)
        self.assertEqual(reading.phase_power_current_l2, None)
        self.assertEqual(reading.phase_power_current_l3, None)

        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, 6)
        self.assertEqual(meter_statistics.long_power_failure_count, 3)
        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)
Пример #6
0
    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)
Пример #7
0
    def test_reading_values(self, now_mock):
        """ Test whether dsmr_datalogger reads the correct values. """
        now_mock.return_value = timezone.make_aware(
            timezone.datetime(2019, 11, 8, hour=20))

        self._fake_dsmr_reading()
        self.assertTrue(DsmrReading.objects.exists())
        reading = DsmrReading.objects.get()

        self.assertEqual(reading.timestamp,
                         datetime(2019, 11, 8, 13, 55, 19, tzinfo=pytz.UTC))
        self.assertEqual(reading.electricity_delivered_1, Decimal('1043.936'))
        self.assertEqual(reading.electricity_returned_1, Decimal('0'))
        self.assertEqual(reading.electricity_delivered_2, Decimal('870.706'))
        self.assertEqual(reading.electricity_returned_2, Decimal('0'))
        self.assertEqual(reading.electricity_currently_delivered,
                         Decimal('0.189'))
        self.assertEqual(reading.electricity_currently_returned, Decimal('0'))
        self.assertEqual(reading.extra_device_timestamp,
                         datetime(2019, 11, 8, 13, 55, 5, tzinfo=pytz.UTC))
        self.assertEqual(reading.extra_device_delivered, Decimal('241.773'))
        self.assertEqual(reading.phase_voltage_l1, Decimal('235.4'))
        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)

        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, 8)
        self.assertEqual(meter_statistics.long_power_failure_count, 3)
        self.assertEqual(meter_statistics.voltage_sag_count_l1, 5)
        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, 1)
        self.assertEqual(meter_statistics.voltage_swell_count_l2, None)
        self.assertEqual(meter_statistics.voltage_swell_count_l3, None)
Пример #8
0
    def get(self, request):
        data = {}

        try:
            latest_reading = DsmrReading.objects.all().order_by('-pk')[0]
        except IndexError:
            # Don't even bother when no data available.
            return HttpResponse(json.dumps(data), content_type='application/json')

        data['timestamp'] = naturaltime(latest_reading.timestamp)
        data['currently_delivered'] = int(latest_reading.electricity_currently_delivered * 1000)
        data['currently_returned'] = int(latest_reading.electricity_currently_returned * 1000)

        try:
            # This WILL fail when we either have no prices at all or conflicting ranges.
            prices = EnergySupplierPrice.objects.by_date(target_date=timezone.now().date())
        except (EnergySupplierPrice.DoesNotExist, EnergySupplierPrice.MultipleObjectsReturned):
            return HttpResponse(json.dumps(data), content_type='application/json')

        # We need to current tariff to get the right price.
        tariff = MeterStatistics.get_solo().electricity_tariff
        currently_delivered = latest_reading.electricity_currently_delivered
        cost_per_hour = None

        tariff_map = {
            1: prices.electricity_1_price,
            2: prices.electricity_2_price,
        }

        try:
            cost_per_hour = currently_delivered * tariff_map[tariff]
        except KeyError:
            pass
        else:
            data['latest_electricity_cost'] = formats.number_format(
                dsmr_consumption.services.round_decimal(cost_per_hour)
            )

        return HttpResponse(json.dumps(data), content_type='application/json')
    def test_reading_values(self, now_mock):
        """ Test whether dsmr_datalogger reads the correct values. """
        now_mock.return_value = timezone.make_aware(
            timezone.datetime(2020, 3, 5))
        self._fake_dsmr_reading()
        self.assertTrue(DsmrReading.objects.exists())
        reading = DsmrReading.objects.get()
        self.assertEqual(reading.timestamp,
                         datetime(2020, 3, 5, 21, 29, 45, tzinfo=pytz.UTC))
        self.assertEqual(reading.electricity_delivered_1, Decimal('172.987'))
        self.assertEqual(reading.electricity_returned_1, Decimal('23.457'))
        self.assertEqual(reading.electricity_delivered_2, Decimal('160.643'))
        self.assertEqual(reading.electricity_returned_2, Decimal('4.819'))
        self.assertEqual(reading.electricity_currently_delivered,
                         Decimal('0.638'))
        self.assertEqual(reading.electricity_currently_returned, Decimal('0'))
        self.assertEqual(reading.extra_device_timestamp,
                         None)  # Error handled.
        self.assertEqual(reading.extra_device_delivered,
                         None)  # Should be NONE too due to timestamp.
        self.assertEqual(reading.phase_voltage_l1, Decimal('230.3'))
        self.assertEqual(reading.phase_voltage_l2, Decimal('230.5'))
        self.assertEqual(reading.phase_voltage_l3, Decimal('229.3'))
        self.assertEqual(reading.phase_power_current_l1, 0)
        self.assertEqual(reading.phase_power_current_l2, 0)
        self.assertEqual(reading.phase_power_current_l3, 1)

        meter_statistics = MeterStatistics.get_solo()
        self.assertIsNone(meter_statistics.dsmr_version)
        self.assertEqual(meter_statistics.electricity_tariff, 2)
        self.assertEqual(meter_statistics.power_failure_count, None)
        self.assertEqual(meter_statistics.long_power_failure_count, None)
        self.assertEqual(meter_statistics.voltage_sag_count_l1, None)
        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, None)
        self.assertEqual(meter_statistics.voltage_swell_count_l2, None)
        self.assertEqual(meter_statistics.voltage_swell_count_l3, None)
Пример #10
0
    def test_reading_values(self, now_mock):
        """ Test whether dsmr_datalogger reads the correct values. """
        now_mock.return_value = timezone.make_aware(
            timezone.datetime(2016, 4, 10, hour=14, minute=30, second=15))

        self._fake_dsmr_reading()
        self.assertTrue(DsmrReading.objects.exists())
        reading = DsmrReading.objects.get()
        self.assertEqual(reading.timestamp,
                         datetime(2016, 4, 10, 12, 30, 15, tzinfo=pytz.UTC))
        self.assertEqual(reading.electricity_delivered_1, Decimal('1234.784'))
        self.assertEqual(reading.electricity_returned_1, Decimal('0'))
        self.assertEqual(reading.electricity_delivered_2, Decimal('4321.725'))
        self.assertEqual(reading.electricity_returned_2, Decimal('0.002'))
        self.assertEqual(reading.electricity_currently_delivered,
                         Decimal('0.36'))
        self.assertEqual(reading.electricity_currently_returned, Decimal('0'))
        self.assertEqual(reading.extra_device_timestamp,
                         datetime(2016, 4, 10, 11, 0, 0, tzinfo=pytz.UTC))
        self.assertEqual(reading.extra_device_delivered, Decimal('7890.693'))
        self.assertIsNone(reading.phase_voltage_l1)
        self.assertIsNone(reading.phase_voltage_l2)
        self.assertIsNone(reading.phase_voltage_l3)
        self.assertIsNone(reading.phase_power_current_l1)
        self.assertIsNone(reading.phase_power_current_l2)
        self.assertIsNone(reading.phase_power_current_l3)

        meter_statistics = MeterStatistics.get_solo()
        self.assertIsNone(meter_statistics.dsmr_version)
        self.assertEqual(meter_statistics.electricity_tariff, 1)
        self.assertEqual(meter_statistics.power_failure_count, None)
        self.assertEqual(meter_statistics.long_power_failure_count, None)
        self.assertEqual(meter_statistics.voltage_sag_count_l1, None)
        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, None)
        self.assertEqual(meter_statistics.voltage_swell_count_l2, None)
        self.assertEqual(meter_statistics.voltage_swell_count_l3, None)
Пример #11
0
    def test_reading_values(self, now_mock):
        """ Test whether dsmr_datalogger reads the correct values. """
        now_mock.return_value = timezone.make_aware(
            timezone.datetime(2020, 1, 1))
        self._fake_dsmr_reading()
        self.assertTrue(DsmrReading.objects.exists())
        reading = DsmrReading.objects.get()
        self.assertEqual(reading.timestamp,
                         datetime(2019, 8, 21, 19, 0, 25, tzinfo=pytz.UTC))
        self.assertEqual(reading.electricity_delivered_1, Decimal('260.129'))
        self.assertEqual(reading.electricity_returned_1, Decimal('0.010'))
        self.assertEqual(reading.electricity_delivered_2, Decimal('338.681'))
        self.assertEqual(reading.electricity_returned_2, Decimal('0.425'))
        self.assertEqual(reading.electricity_currently_delivered,
                         Decimal('0.261'))
        self.assertEqual(reading.electricity_currently_returned, Decimal('0'))
        self.assertEqual(reading.extra_device_timestamp,
                         datetime(2019, 8, 21, 19, 0, 11, tzinfo=pytz.UTC))
        self.assertEqual(reading.extra_device_delivered, Decimal('29.553'))
        self.assertEqual(reading.phase_voltage_l1, Decimal('231.0'))
        self.assertEqual(reading.phase_voltage_l2, Decimal('0'))
        self.assertEqual(reading.phase_voltage_l3, Decimal('230.9'))
        self.assertEqual(reading.phase_power_current_l1, 0)
        self.assertEqual(reading.phase_power_current_l2, 0)
        self.assertEqual(reading.phase_power_current_l3, 0)

        meter_statistics = MeterStatistics.get_solo()
        self.assertIsNone(meter_statistics.dsmr_version)
        self.assertEqual(meter_statistics.electricity_tariff, 2)
        self.assertEqual(meter_statistics.power_failure_count, None)
        self.assertEqual(meter_statistics.long_power_failure_count, None)
        self.assertEqual(meter_statistics.voltage_sag_count_l1, None)
        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, None)
        self.assertEqual(meter_statistics.voltage_swell_count_l2, None)
        self.assertEqual(meter_statistics.voltage_swell_count_l3, None)
Пример #12
0
    def test_reading_values(self, now_mock):
        """ Test whether dsmr_datalogger reads the correct values. """
        now_mock.return_value = timezone.make_aware(
            timezone.datetime(2021, 1, 1))
        self._fake_dsmr_reading()
        self.assertTrue(DsmrReading.objects.exists())
        reading = DsmrReading.objects.get()
        self.assertEqual(reading.timestamp,
                         datetime(2020, 8, 7, 6, 27, 11, tzinfo=pytz.UTC))
        self.assertEqual(reading.electricity_delivered_1, Decimal('1924.771'))
        self.assertEqual(reading.electricity_returned_1, Decimal('1968.710'))
        self.assertEqual(reading.electricity_delivered_2, Decimal('2549.919'))
        self.assertEqual(reading.electricity_returned_2, Decimal('692.984'))
        self.assertEqual(reading.electricity_currently_delivered, Decimal('0'))
        self.assertEqual(reading.electricity_currently_returned,
                         Decimal('0.611'))
        self.assertEqual(reading.extra_device_timestamp,
                         datetime(2020, 8, 7, 6, 25, 2, tzinfo=pytz.UTC))
        self.assertEqual(reading.extra_device_delivered, Decimal('1414.287'))
        self.assertEqual(reading.phase_voltage_l1, Decimal('235.6'))
        self.assertEqual(reading.phase_voltage_l2, None)
        self.assertEqual(reading.phase_voltage_l3, None)
        self.assertEqual(reading.phase_power_current_l1, 2)
        self.assertEqual(reading.phase_power_current_l2, None)
        self.assertEqual(reading.phase_power_current_l3, None)

        meter_statistics = MeterStatistics.get_solo()
        self.assertIsNone(meter_statistics.dsmr_version)
        self.assertEqual(meter_statistics.electricity_tariff, 1)
        self.assertEqual(meter_statistics.power_failure_count, None)
        self.assertEqual(meter_statistics.long_power_failure_count, None)
        self.assertEqual(meter_statistics.voltage_sag_count_l1, None)
        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, None)
        self.assertEqual(meter_statistics.voltage_swell_count_l2, None)
        self.assertEqual(meter_statistics.voltage_swell_count_l3, None)
Пример #13
0
    def test_reading_values(self, now_mock):
        """ Test whether dsmr_datalogger reads the correct values. """
        now_mock.return_value = timezone.make_aware(
            timezone.datetime(2020, 1, 1))
        self._fake_dsmr_reading()
        self.assertTrue(DsmrReading.objects.exists())
        reading = DsmrReading.objects.get()
        self.assertEqual(reading.timestamp,
                         datetime(2019, 10, 31, 13, 22, 39, tzinfo=pytz.UTC))
        self.assertEqual(reading.electricity_delivered_1, Decimal('5675.956'))
        self.assertEqual(reading.electricity_returned_1, Decimal('0.002'))
        self.assertEqual(reading.electricity_delivered_2, Decimal('0'))
        self.assertEqual(reading.electricity_returned_2, Decimal('0'))
        self.assertEqual(reading.electricity_currently_delivered,
                         Decimal('3.074'))
        self.assertEqual(reading.electricity_currently_returned, Decimal('0'))
        self.assertIsNone(reading.extra_device_timestamp)
        self.assertIsNone(reading.extra_device_delivered)
        self.assertIsNone(reading.phase_voltage_l1)
        self.assertIsNone(reading.phase_voltage_l2)
        self.assertIsNone(reading.phase_voltage_l3)
        self.assertIsNone(reading.phase_power_current_l1)
        self.assertIsNone(reading.phase_power_current_l2)
        self.assertIsNone(reading.phase_power_current_l3)

        meter_statistics = MeterStatistics.get_solo()
        self.assertEqual(meter_statistics.dsmr_version, '42')
        self.assertIsNone(meter_statistics.electricity_tariff)
        self.assertIsNone(meter_statistics.power_failure_count)
        self.assertIsNone(meter_statistics.long_power_failure_count)
        self.assertIsNone(meter_statistics.voltage_sag_count_l1)
        self.assertIsNone(meter_statistics.voltage_sag_count_l2)
        self.assertIsNone(meter_statistics.voltage_sag_count_l3)
        self.assertIsNone(meter_statistics.voltage_swell_count_l1)
        self.assertIsNone(meter_statistics.voltage_swell_count_l2)
        self.assertIsNone(meter_statistics.voltage_swell_count_l3)
Пример #14
0
    def get_context_data(self, **kwargs):
        context_data = super(Statistics, self).get_context_data(**kwargs)
        context_data[
            'capabilities'] = dsmr_backend.services.backend.get_capabilities()
        context_data[
            'electricity_statistics'] = ElectricityStatistics.get_solo(
            ).export()
        context_data['frontend_settings'] = FrontendSettings.get_solo()

        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
Пример #15
0
def publish_split_topic_meter_statistics():
    """ Publishes meter statistics to a broker, formatted in a separate topic per field name, if set and enabled. """
    split_topic_settings = meter_statistics.SplitTopicMeterStatisticsMQTTSettings.get_solo(
    )

    if not split_topic_settings.enabled:
        return

    # User specified formatting.
    config_parser = configparser.ConfigParser()
    config_parser.read_string(split_topic_settings.formatting)
    topic_mapping = config_parser['mapping']

    serialized_reading = json.loads(
        serializers.serialize('json', [MeterStatistics.get_solo()]))
    reading_fields = dict(serialized_reading[0]['fields'].items())
    reading_fields['id'] = serialized_reading[0]['pk']

    # Copy all fields described in the mapping.
    for k, v in reading_fields.items():
        if k not in topic_mapping:
            continue

        queue.Message.objects.create(topic=topic_mapping[k], payload=v)
Пример #16
0
def publish_split_topic_meter_statistics():
    """ Publishes meter statistics to a broker, formatted in a separate topic per field name, if set and enabled. """
    split_topic_settings = meter_statistics.SplitTopicMeterStatisticsMQTTSettings.get_solo(
    )

    if not split_topic_settings.enabled:
        return

    # User specified formatting.
    config_parser = configparser.ConfigParser()
    config_parser.read_string(split_topic_settings.formatting)
    topic_mapping = config_parser['mapping']

    mqtt_messages = []
    serialized_reading = json.loads(
        serializers.serialize('json', [MeterStatistics.get_solo()]))
    reading_fields = dict(serialized_reading[0]['fields'].items())
    reading_fields['id'] = serialized_reading[0]['pk']

    # Copy all fields described in the mapping.
    for k, v in reading_fields.items():
        if k not in topic_mapping:
            continue

        mqtt_messages.append({
            'topic': topic_mapping[k],
            'payload': v,
        })

    broker_kwargs = get_broker_configuration()

    try:
        publish.multiple(msgs=mqtt_messages, **broker_kwargs)
    except ValueError as error:
        logger.error(
            'MQTT publish_split_topic_meter_statistics() | {}'.format(error))
Пример #17
0
 def setUp(self):
     self.instance = MeterStatistics.get_solo()
Пример #18
0
def live_electricity_consumption() -> Dict:
    """ Returns the current latest/live electricity consumption. """
    data = {}

    try:
        latest_reading = DsmrReading.objects.all().order_by('-timestamp')[0]
    except IndexError:
        return data

    latest_timestamp = timezone.localtime(latest_reading.timestamp)

    # In case the smart meter's clock is running in the future.
    latest_timestamp = min(timezone.now(), latest_timestamp)

    data['timestamp'] = latest_timestamp
    data['currently_delivered'] = int(
        latest_reading.electricity_currently_delivered * 1000)
    data['currently_returned'] = int(
        latest_reading.electricity_currently_returned * 1000)
    data['cost_per_hour'] = None
    data['tariff_name'] = None

    tariff = MeterStatistics.get_solo().electricity_tariff
    frontend_settings = FrontendSettings.get_solo()
    tariff_names = {
        1: frontend_settings.tariff_1_delivered_name.capitalize(),
        2: frontend_settings.tariff_2_delivered_name.capitalize(),
    }

    try:
        data['tariff_name'] = tariff_names[tariff]
    except KeyError:
        pass

    try:
        prices = get_day_prices(day=timezone.now().date())
    except EnergySupplierPrice.DoesNotExist:
        return data

    delivered_prices_per_tariff = {
        1: prices.electricity_delivered_1_price,
        2: prices.electricity_delivered_2_price,
    }
    returned_prices_per_tariff = {
        1: prices.electricity_returned_1_price,
        2: prices.electricity_returned_2_price,
    }

    try:
        delivered_cost_per_hour = latest_reading.electricity_currently_delivered * delivered_prices_per_tariff[
            tariff]
        returned_cost_per_hour = latest_reading.electricity_currently_returned * returned_prices_per_tariff[
            tariff]
    except KeyError:
        return data

    # Some users have a setup that delivers and returns simultaneously. So we need to take both into account.
    data['cost_per_hour'] = formats.number_format(
        round_decimal(delivered_cost_per_hour - returned_cost_per_hour))

    return data
Пример #19
0
 def get_object(self):
     # This is a REALLY good combo with django-solo, as it fits perfectly for a singleton retreiver and updater!
     return MeterStatistics.get_solo()
Пример #20
0
def _map_telegram_to_model(parsed_telegram, data):
    """ Maps parsed telegram to the fields. """
    READING_FIELDS = [
        x.name for x in DsmrReading._meta.get_fields()
        if x.name not in ('id', 'processed')
    ]
    STATISTICS_FIELDS = [
        x.name for x in MeterStatistics._meta.get_fields()
        if x.name not in ('id', 'rejected_telegrams', 'latest_telegram')
    ]

    model_fields = {k: None for k in READING_FIELDS + STATISTICS_FIELDS}
    mapping = _get_dsmrreader_mapping(
        DataloggerSettings.get_solo().dsmr_version)

    for obis_ref, obis_data in parsed_telegram.items():
        try:
            # Skip any fields we're not storing in our system.
            target_field = mapping[obis_ref]
        except KeyError:
            continue

        if isinstance(target_field, dict):
            model_fields[target_field['value']] = obis_data.value
            model_fields[target_field['datetime']] = obis_data.datetime
        else:
            model_fields[target_field] = obis_data.value

    # Defaults for telegrams with missing data.
    model_fields['timestamp'] = model_fields['timestamp'] or timezone.now()
    model_fields['electricity_delivered_2'] = model_fields[
        'electricity_delivered_2'] or 0
    model_fields[
        'electricity_returned_2'] = model_fields['electricity_returned_2'] or 0

    # Hack for invalid dates on device bus. Reset the delivered value as well.
    if model_fields['extra_device_timestamp'] is None:
        model_fields['extra_device_delivered'] = None

    # For some reason, there are telegrams generated with a timestamp in the far future. We should disallow that.
    discard_timestamp = timezone.now() + timezone.timedelta(hours=24)

    if model_fields['timestamp'] > discard_timestamp or (
            model_fields['extra_device_timestamp'] is not None
            and model_fields['extra_device_timestamp'] > discard_timestamp):
        error_message = 'Discarded telegram with future timestamp(s): {} / {}'.format(
            model_fields['timestamp'], model_fields['extra_device_timestamp'])
        logger.error(error_message)
        raise InvalidTelegramError(error_message)

    # Now we need to split reading & statistics. So we split the dict here.
    reading_kwargs = {k: model_fields[k] for k in READING_FIELDS}
    statistics_kwargs = {k: model_fields[k] for k in STATISTICS_FIELDS}

    # Reading will be processed later.
    new_instance = DsmrReading.objects.create(**reading_kwargs)

    # There should already be one in database, created when migrating.
    statistics_kwargs['latest_telegram'] = data
    MeterStatistics.get_solo().update(
        **statistics_kwargs)  # Update() is required for signal!

    # Creation should be completed, can now be broadcasted for post processing.
    dsmr_datalogger.signals.raw_telegram.send_robust(None, data=data)
    dsmr_datalogger.signals.dsmr_reading_created.send_robust(
        None, instance=new_instance)

    return new_instance
Пример #21
0
def _map_telegram_to_model(parsed_telegram: Dict, data: str):
    """ Maps parsed telegram to the fields. """
    READING_FIELDS = [
        x.name for x in DsmrReading._meta.get_fields()
        if x.name not in ('id', 'processed')
    ]
    STATISTICS_FIELDS = [
        x.name for x in MeterStatistics._meta.get_fields()
        if x.name not in ('id', 'rejected_telegrams', 'latest_telegram')
    ]

    datalogger_settings = DataloggerSettings.get_solo()
    model_fields = {k: None for k in READING_FIELDS + STATISTICS_FIELDS}
    mapping = _get_dsmrreader_mapping(datalogger_settings.dsmr_version)

    for obis_ref, obis_data in parsed_telegram.items():
        try:
            # Skip any fields we're not storing in our system.
            target_field = mapping[obis_ref]
        except KeyError:
            continue

        if isinstance(target_field, dict):
            model_fields[target_field['value']] = obis_data.value
            model_fields[target_field['datetime']] = obis_data.datetime
        else:
            model_fields[target_field] = obis_data.value

    # Defaults for telegrams with missing data.
    model_fields['timestamp'] = model_fields['timestamp'] or timezone.now()
    model_fields['electricity_delivered_2'] = model_fields[
        'electricity_delivered_2'] or 0  # type:ignore[assignment]
    model_fields['electricity_returned_2'] = model_fields[
        'electricity_returned_2'] or 0  # type:ignore[assignment]

    # Ignore invalid dates on device bus. Reset the delivered value as well. This MUST be checked before override below.
    if model_fields['extra_device_timestamp'] is None:
        model_fields['extra_device_delivered'] = None

    # This optional setting fixes some rare situations where the smart meter's internal clock is incorrect.
    if datalogger_settings.override_telegram_timestamp:
        now = timezone.now()

        logger.debug(
            "WARNING: Overriding telegram timestamps due to configuration")
        model_fields['timestamp'] = now

        if model_fields['extra_device_timestamp'] is not None:
            # WARNING: So None (v2, v3, Fluvius) default to v4 behaviour.
            is_v5 = model_fields['dsmr_version'] is not None and model_fields[
                'dsmr_version'].startswith('5')

            model_fields[
                'extra_device_timestamp'] = calculate_fake_gas_reading_timestamp(
                    now=now, is_dsmr_v5=is_v5)

    # Fix for rare smart meters with a timestamp in the far future. We should disallow that.
    discard_after = timezone.now() + timezone.timedelta(hours=24)

    if model_fields['timestamp'] > discard_after or (
            model_fields['extra_device_timestamp'] is not None
            and model_fields['extra_device_timestamp'] > discard_after):
        error_message = 'Discarded telegram with future timestamp(s): {} / {}'.format(
            model_fields['timestamp'], model_fields['extra_device_timestamp'])
        logger.error(error_message)
        raise InvalidTelegramError(error_message)

    # Now we need to split reading & statistics. So we split the dict here.
    reading_kwargs = {k: model_fields[k] for k in READING_FIELDS}
    statistics_kwargs = {k: model_fields[k] for k in STATISTICS_FIELDS}

    # Reading will be processed later.
    new_instance = DsmrReading.objects.create(**reading_kwargs)

    # There should already be one in database, created when migrating.
    statistics_kwargs['latest_telegram'] = data  # type:ignore[assignment]
    MeterStatistics.get_solo().update(
        **statistics_kwargs)  # Update() is required for signal!

    # Creation should be completed, can now be broadcasted for post processing.
    dsmr_datalogger.signals.raw_telegram.send_robust(None, data=data)
    dsmr_datalogger.signals.dsmr_reading_created.send_robust(
        None, instance=new_instance)

    return new_instance
Пример #22
0
 def setUp(self):
     self.assertEqual(DsmrReading.objects.all().count(), 6)
     self.assertTrue(DsmrReading.objects.unprocessed().exists())
     ConsumptionSettings.get_solo()
     MeterStatistics.get_solo()
     MeterStatistics.objects.all().update(dsmr_version='50')
Пример #23
0
 def test_ordering(self):
     """ Test whether defaults allow the creation of any empty model. """
     MeterStatistics.get_solo()
     self.assertTrue(MeterStatistics.objects.exists())
Пример #24
0
    def test_patch(self, now_mock):
        now_mock.return_value = timezone.make_aware(
            timezone.datetime(2020, 1, 1))

        instance = MeterStatistics.get_solo()
        EXPECTED = dict(
            dsmr_version=None,
            electricity_tariff=None,
            power_failure_count=None,
            long_power_failure_count=None,
            voltage_sag_count_l1=None,
            voltage_sag_count_l2=None,
            voltage_sag_count_l3=None,
            voltage_swell_count_l1=None,
            voltage_swell_count_l2=None,
            voltage_swell_count_l3=None,
        )

        # Check default (empty) state in database.
        [
            self.assertEqual(getattr(instance, k), expected_value, k)
            for k, expected_value in EXPECTED.items()
        ]

        # Partial update.
        self._request('meter-statistics',
                      expected_code=200,
                      method='patch',
                      data=dict(
                          timestamp='2020-01-15T12:34:56+01:00',
                          dsmr_version='50',
                          electricity_tariff=1,
                          power_failure_count=123,
                          long_power_failure_count=456,
                      ))

        EXPECTED = dict(
            dsmr_version='50',
            electricity_tariff=1,
            power_failure_count=123,
            long_power_failure_count=456,
            voltage_sag_count_l1=None,
            voltage_sag_count_l2=None,
            voltage_sag_count_l3=None,
            voltage_swell_count_l1=None,
            voltage_swell_count_l2=None,
            voltage_swell_count_l3=None,
        )

        # Check partial updated state in database.
        instance.refresh_from_db()
        [
            self.assertEqual(getattr(instance, k), expected_value, k)
            for k, expected_value in EXPECTED.items()
        ]
        self.assertEqual(str(instance.timestamp),
                         '2020-01-15 11:34:56+00:00')  # Shifted to UTC

        self._request('meter-statistics',
                      expected_code=200,
                      method='patch',
                      data=dict(
                          dsmr_version='42',
                          electricity_tariff=2,
                          power_failure_count=77777,
                          long_power_failure_count=8888,
                          voltage_sag_count_l1=11,
                          voltage_sag_count_l2=22,
                          voltage_sag_count_l3=33,
                          voltage_swell_count_l1=44,
                          voltage_swell_count_l2=55,
                          voltage_swell_count_l3=66,
                      ))

        EXPECTED = dict(
            dsmr_version='42',
            electricity_tariff=2,
            power_failure_count=77777,
            long_power_failure_count=8888,
            voltage_sag_count_l1=11,
            voltage_sag_count_l2=22,
            voltage_sag_count_l3=33,
            voltage_swell_count_l1=44,
            voltage_swell_count_l2=55,
            voltage_swell_count_l3=66,
        )

        # Check final updated state in database.
        instance.refresh_from_db()
        [
            self.assertEqual(getattr(instance, k), expected_value, k)
            for k, expected_value in EXPECTED.items()
        ]