示例#1
0
    def test_export_okay(self, send_robust_mock, now_mock, export_data_mock,
                         should_export_mock, requests_post_mock):
        """ Test export() as designed. """
        now_mock.return_value = timezone.make_aware(
            timezone.datetime(2017, 10, 1, hour=15))
        export_data_mock.return_value = {
            'x': 'y'
        }  # Unimportant for this test.

        should_export_mock.return_value = True
        requests_post_mock.return_value = mock.MagicMock(status_code=200,
                                                         text='Fake accept')
        self._apply_fake_settings()

        self.assertFalse(requests_post_mock.called)
        self.assertFalse(send_robust_mock.called)

        dsmr_pvoutput.services.export()

        self.assertIsNotNone(PVOutputAddStatusSettings.get_solo().next_export)
        self.assertTrue(requests_post_mock.called)
        self.assertTrue(send_robust_mock.called)

        # Check API parameters.
        api_settings = PVOutputAPISettings.get_solo()
        requests_post_mock.assert_called_once_with(
            PVOutputAddStatusSettings.API_URL,
            headers={
                'X-Pvoutput-Apikey': api_settings.auth_token,
                'X-Pvoutput-SystemId': api_settings.system_identifier,
            },
            data={'x': 'y'},
        )

        # With processing delay as well.
        requests_post_mock.reset_mock()
        send_robust_mock.reset_mock()
        PVOutputAddStatusSettings.objects.update(processing_delay=5,
                                                 next_export=None)

        dsmr_pvoutput.services.export()

        status_settings = PVOutputAddStatusSettings.get_solo()
        self.assertIsNotNone(status_settings.next_export)
        self.assertEqual(status_settings.latest_sync, timezone.now())
        self.assertTrue(requests_post_mock.called)
        self.assertTrue(send_robust_mock.called)

        api_settings = PVOutputAPISettings.get_solo()
        requests_post_mock.assert_called_once_with(
            PVOutputAddStatusSettings.API_URL,
            headers={
                'X-Pvoutput-Apikey': api_settings.auth_token,
                'X-Pvoutput-SystemId': api_settings.system_identifier,
            },
            data={
                'x': 'y',
                'delay': 5,
            },
        )
示例#2
0
 def _apply_fake_settings(self):
     PVOutputAPISettings.get_solo().update(
         auth_token='XXXXX',
         system_identifier=12345,
     )
     PVOutputAddStatusSettings.get_solo().update(export=True,
                                                 upload_delay=1)
示例#3
0
    def test_export_okay(self, send_robust_mock, now_mock, export_data_mock,
                         requests_post_mock):
        """ Test export() as designed. """
        now_mock.return_value = timezone.make_aware(
            timezone.datetime(2017, 10, 1, hour=15))
        export_data_mock.return_value = {
            'x': 'y'
        }  # Unimportant for this test.

        requests_post_mock.return_value = mock.MagicMock(status_code=200,
                                                         text='Fake accept')
        self._apply_fake_settings()

        self.assertFalse(requests_post_mock.called)
        self.assertFalse(send_robust_mock.called)

        dsmr_pvoutput.services.run(scheduled_process=self.schedule_process)

        self.assertTrue(requests_post_mock.called)
        self.assertTrue(send_robust_mock.called)

        # Check API parameters.
        api_settings = PVOutputAPISettings.get_solo()
        requests_post_mock.assert_called_once_with(
            PVOutputAddStatusSettings.API_URL,
            headers={
                'User-Agent': settings.DSMRREADER_USER_AGENT,
                'X-Pvoutput-Apikey': api_settings.auth_token,
                'X-Pvoutput-SystemId': api_settings.system_identifier,
            },
            data={'x': 'y'},
            timeout=settings.DSMRREADER_CLIENT_TIMEOUT,
        )

        # With processing delay as well.
        requests_post_mock.reset_mock()
        send_robust_mock.reset_mock()
        self.schedule_process.reschedule_asap()
        PVOutputAddStatusSettings.objects.update(processing_delay=5)

        dsmr_pvoutput.services.run(scheduled_process=self.schedule_process)

        self.assertTrue(requests_post_mock.called)
        self.assertTrue(send_robust_mock.called)

        api_settings = PVOutputAPISettings.get_solo()
        requests_post_mock.assert_called_once_with(
            PVOutputAddStatusSettings.API_URL,
            headers={
                'User-Agent': settings.DSMRREADER_USER_AGENT,
                'X-Pvoutput-Apikey': api_settings.auth_token,
                'X-Pvoutput-SystemId': api_settings.system_identifier,
            },
            data={
                'x': 'y',
                'delay': 5,
            },
            timeout=settings.DSMRREADER_CLIENT_TIMEOUT,
        )
示例#4
0
    def test_export_not_allowed(self, post_mock):
        """ Test export() blocking behaviour. """
        self._apply_fake_settings()
        PVOutputAddStatusSettings.get_solo().update(export=False)
        dsmr_pvoutput.services.run(scheduled_process=self.schedule_process)
        self.assertFalse(post_mock.called)

        self._apply_fake_settings()
        PVOutputAPISettings.get_solo().update(auth_token='')
        dsmr_pvoutput.services.run(scheduled_process=self.schedule_process)
        self.assertFalse(post_mock.called)

        self._apply_fake_settings()
        PVOutputAPISettings.get_solo().update(system_identifier='')
        dsmr_pvoutput.services.run(scheduled_process=self.schedule_process)
        self.assertFalse(post_mock.called)
示例#5
0
def export():
    """ Exports data to PVOutput, calling Add Status. """
    if not should_export():
        return

    api_settings = PVOutputAPISettings.get_solo()
    status_settings = PVOutputAddStatusSettings.get_solo()

    # Find the first and last consumption of today, taking any delay into account.
    local_now = timezone.localtime(timezone.now())
    start = local_now.replace(hour=0, minute=0, second=0)  # Midnight
    end = local_now - timezone.timedelta(minutes=status_settings.upload_delay)

    ecs = ElectricityConsumption.objects.filter(read_at__gte=start,
                                                read_at__lte=end)

    if not ecs.exists():
        print(' [!] PVOutput: No data found for {}'.format(local_now))
        return schedule_next_export()

    first = ecs[0]
    last = ecs.order_by('-read_at')[0]
    diff = last - first  # Custom operator

    total_consumption = diff['delivered_1'] + diff['delivered_2']
    net_power = last.currently_delivered - last.currently_returned  # Negative when returning more Watt than requested.

    consumption_timestamp = timezone.localtime(last.read_at)

    data = {
        'd': consumption_timestamp.date().strftime('%Y%m%d'),
        't': consumption_timestamp.time().strftime('%H:%M'),
        'v3': int(total_consumption * 1000),  # Energy Consumption (Wh)
        'v4': int(net_power * 1000),  # Power Consumption (W)
        'n': 1,  # Net Flag, always enabled for smart meters
    }

    # Optional, paid PVOutput feature.
    if status_settings.processing_delay:
        data.update({'delay': status_settings.processing_delay})

    print(' - PVOutput | Uploading data @ {}'.format(data))
    pvoutput_upload.send_robust(None, data=data)

    response = requests.post(PVOutputAddStatusSettings.API_URL,
                             headers={
                                 'X-Pvoutput-Apikey':
                                 api_settings.auth_token,
                                 'X-Pvoutput-SystemId':
                                 api_settings.system_identifier,
                             },
                             data=data)

    if response.status_code != 200:
        print(' [!] PVOutput upload failed (HTTP {}): {}'.format(
            response.status_code, response.text))

    schedule_next_export()
示例#6
0
    def _apply_fake_settings(self):
        api_settings = PVOutputAPISettings.get_solo()
        api_settings.auth_token = 'XXXXX'
        api_settings.system_identifier = 12345
        api_settings.save()

        status_settings = PVOutputAddStatusSettings.get_solo()
        status_settings.export = True
        status_settings.upload_delay = 1
        status_settings.save()
示例#7
0
def should_export():
    """ Checks whether we should export data yet, for Add Status calls. """
    api_settings = PVOutputAPISettings.get_solo()
    status_settings = PVOutputAddStatusSettings.get_solo()

    # Only when enabled and credentials set.
    if not status_settings.export or not api_settings.auth_token or not api_settings.system_identifier:
        return False

    return dsmr_backend.services.is_timestamp_passed(timestamp=status_settings.next_export)
示例#8
0
def _on_pvoutput_setting_update():
    api_settings = PVOutputAPISettings.get_solo()
    add_status_settings = PVOutputAddStatusSettings.get_solo()

    ScheduledProcess.objects.filter(
        module=settings.DSMRREADER_MODULE_PVOUTPUT_EXPORT).update(
            planned=timezone.now(),
            active=all([
                api_settings.auth_token, api_settings.system_identifier,
                add_status_settings.export
            ]))
示例#9
0
    def test_should_export_default(self, now_mock):
        """ Test should_export() default behaviour. """
        now_mock.return_value = timezone.make_aware(timezone.datetime(2017, 10, 1, hour=15))

        api_settings = PVOutputAPISettings.get_solo()
        status_settings = PVOutputAddStatusSettings.get_solo()

        self.assertIsNone(api_settings.auth_token)
        self.assertFalse(api_settings.system_identifier)
        self.assertFalse(status_settings.export)
        self.assertIsNone(status_settings.next_export)

        self.assertFalse(dsmr_pvoutput.services.should_export())
示例#10
0
def export():
    """ Exports data to PVOutput, calling Add Status. """
    if not should_export():
        return

    api_settings = PVOutputAPISettings.get_solo()
    status_settings = PVOutputAddStatusSettings.get_solo()

    try:
        data = get_export_data(next_export=status_settings.next_export,
                               upload_delay=status_settings.upload_delay)
    except LookupError:
        return

    if not data:
        logger.warning(' [!] PVOutput: No data found (yet)')
        return schedule_next_export()

    # Optional, paid PVOutput feature.
    if status_settings.processing_delay:
        data.update({'delay': status_settings.processing_delay})

    logger.debug('PVOutput: Uploading data: %s', data)
    pvoutput_upload.send_robust(None, data=data)

    response = requests.post(PVOutputAddStatusSettings.API_URL,
                             headers={
                                 'User-Agent':
                                 settings.DSMRREADER_USER_AGENT,
                                 'X-Pvoutput-Apikey':
                                 api_settings.auth_token,
                                 'X-Pvoutput-SystemId':
                                 api_settings.system_identifier,
                             },
                             data=data)

    if response.status_code != 200:
        logger.error(' [!] PVOutput upload failed (HTTP %s): %s',
                     response.status_code, response.text)
    else:
        status_settings.latest_sync = timezone.now()
        status_settings.save()

    schedule_next_export()
示例#11
0
def should_export():
    """ Checks whether we should export data yet, for Add Status calls. """
    api_settings = PVOutputAPISettings.get_solo()
    status_settings = PVOutputAddStatusSettings.get_solo()

    # Only when enabled and credentials set.
    if not status_settings.export or not api_settings.auth_token or not api_settings.system_identifier:
        return False

    # Nonsense when having no data.
    capabilities = dsmr_backend.services.get_capabilities()

    if not capabilities['electricity_returned']:
        print(
            ' - [!] PVOutput | No electricity return recorded by application!')
        return False

    return dsmr_backend.services.is_timestamp_passed(
        timestamp=status_settings.next_export)
示例#12
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