def test_export_fail(self, now_mock, should_export_mock, requests_post_mock): """ Test export() failing by denied API call. """ now_mock.return_value = timezone.make_aware( timezone.datetime(2015, 12, 12, hour=4, minute=45)) should_export_mock.return_value = True settings = MinderGasSettings.get_solo() self.assertFalse(settings.export) self.assertIsNone(settings.next_export) self.assertFalse(requests_post_mock.called) # Mindergas error codes according to docs. for current_error_code in (401, 422): requests_post_mock.return_value = mock.MagicMock( status_code=current_error_code, text='Error message') dsmr_mindergas.services.export() settings = MinderGasSettings.get_solo() # This should be set one hour forward now. self.assertEqual(settings.next_export, timezone.now() + timezone.timedelta(hours=1)) self.assertTrue(requests_post_mock.called)
def test_export_okay(self, now_mock, should_export_mock, requests_post_mock): """ Test export() as designed. """ now_mock.return_value = timezone.make_aware( timezone.datetime(2015, 12, 12, hour=0, minute=5)) should_export_mock.return_value = True requests_post_mock.return_value = mock.MagicMock(status_code=201, text='Fake accept') settings = MinderGasSettings.get_solo() self.assertFalse(settings.export) self.assertIsNone(settings.next_export) self.assertFalse(requests_post_mock.called) dsmr_mindergas.services.export() settings = MinderGasSettings.get_solo() self.assertIsNotNone(settings.next_export) self.assertTrue(requests_post_mock.called) # Check API parameters. requests_post_mock.assert_called_once_with( MinderGasSettings.API_URL, headers={ 'Content-Type': 'application/json', 'AUTH-TOKEN': settings.auth_token }, data=json.dumps({ 'date': '2015-12-11', 'reading': '956.739' }), )
def export(): """ Exports gas readings to the MinderGas website. """ if not should_export(): return print(' - MinderGas | Attempting to upload gas meter position.') # Just post the latest reading of the day before. today = timezone.localtime(timezone.now()) midnight = timezone.make_aware( timezone.datetime( year=today.year, month=today.month, day=today.day, hour=0, )) # Push back for a day and a bit. next_export = midnight + timezone.timedelta(hours=24, minutes=random.randint(15, 59)) try: last_gas_reading = GasConsumption.objects.filter( # Slack of six hours to make sure we have any valid reading at all. read_at__range=(midnight - timezone.timedelta(hours=6), midnight)).order_by('-read_at')[0] except IndexError: # Just continue, even though we have no data... yet. last_gas_reading = None print(' - MinderGas | No gas readings found for uploading') else: settings = MinderGasSettings.get_solo() print(' - MinderGas | Uploading gas meter position: {}'.format( last_gas_reading.delivered)) # Register telegram by simply sending it to the application with a POST request. response = requests.post( MinderGasSettings.API_URL, headers={ 'Content-Type': 'application/json', 'AUTH-TOKEN': settings.auth_token }, data=json.dumps({ 'date': last_gas_reading.read_at.date().isoformat(), 'reading': str(last_gas_reading.delivered) }), ) if response.status_code != 201: # Try again in an hour. next_export = timezone.now() + timezone.timedelta(hours=1) print(' [!] MinderGas upload failed (HTTP {}): {}'.format( response.status_code, response.text)) print(' - MinderGas | Delaying the next upload until: {}'.format( next_export)) settings = MinderGasSettings.get_solo() settings.next_export = next_export settings.save()
def run(scheduled_process): mindergas_settings = MinderGasSettings.get_solo() # Only when enabled and token set. if not mindergas_settings.auth_token: return mindergas_settings.update( export=False) # Should also disable SP. # Nonsense when having no data. if not dsmr_backend.services.backend.get_capabilities(capability='gas'): return scheduled_process.delay(timezone.timedelta(hours=1)) try: export() except Exception as error: logger.exception(error) scheduled_process.delay(timezone.timedelta(hours=1)) return dsmr_frontend.services.display_dashboard_message( message=_('Failed to export to MinderGas: {}'.format(error))) # Reschedule between 3 AM and 6 AM next day. midnight = timezone.localtime( timezone.make_aware(timezone.datetime.combine(timezone.now(), time.min))) next_midnight = midnight + timezone.timedelta( hours=dsmr_backend.services.backend.hours_in_day( day=timezone.now().date())) scheduled_process.reschedule(next_midnight + timezone.timedelta( hours=random.randint(3, 5), minutes=random.randint(15, 59)))
def status_info(): """ Returns the status info of the application. """ capabilities = get_capabilities() status = { 'capabilities': capabilities, 'electricity': get_electricity_status(capabilities), 'gas': get_gas_status(capabilities), 'readings': get_reading_status(), 'statistics': get_statistics_status(), 'tools': { 'backup': { 'enabled': False, 'latest_backup': None, }, 'dropbox': { 'enabled': False, 'latest_sync': None, }, 'pvoutput': { 'enabled': False, 'latest_sync': None, }, 'mindergas': { 'enabled': False, 'latest_sync': None, }, 'mqtt': get_mqtt_status(), } } # (External) tools below. backup_settings = BackupSettings.get_solo() if backup_settings.daily_backup: status['tools']['backup']['enabled'] = True status['tools']['backup'][ 'latest_backup'] = backup_settings.latest_backup dropbox_settings = DropboxSettings.get_solo() if dropbox_settings.access_token: status['tools']['dropbox']['enabled'] = True status['tools']['dropbox'][ 'latest_sync'] = dropbox_settings.latest_sync pvoutput_settings = PVOutputAddStatusSettings.get_solo() if pvoutput_settings.export: status['tools']['pvoutput']['enabled'] = True status['tools']['pvoutput'][ 'latest_sync'] = pvoutput_settings.latest_sync mindergas_settings = MinderGasSettings.get_solo() if mindergas_settings.export: status['tools']['mindergas']['enabled'] = True status['tools']['mindergas'][ 'latest_sync'] = mindergas_settings.latest_sync return status
def export(): """ Exports gas readings to the MinderGas website. """ mindergas_settings = MinderGasSettings.get_solo() midnight = timezone.localtime( timezone.make_aware(timezone.datetime.combine(timezone.now(), time.min))) try: last_gas_reading = GasConsumption.objects.filter( # Slack of a few hours to make sure we have any valid reading at all. read_at__range=(midnight - timezone.timedelta(hours=3), midnight)).order_by('-read_at')[0] except IndexError: raise AssertionError(_('No recent gas reading found')) reading_date = last_gas_reading.read_at.date().isoformat() logger.debug('MinderGas: Uploading gas meter position: %s m3 @ %s', last_gas_reading.delivered, reading_date) response = requests.post( MinderGasSettings.API_URL, headers={ 'User-Agent': settings.DSMRREADER_USER_AGENT, 'Content-Type': 'application/json', 'AUTH-TOKEN': mindergas_settings.auth_token }, data=json.dumps({ 'date': reading_date, 'reading': str(last_gas_reading.delivered) }), ) if response.status_code != 201: logger.error('MinderGas: Upload failed (HTTP %s): %s', response.status_code, response.text) raise AssertionError(_('Unexpected status code received'))
def export(): """ Exports gas readings to the MinderGas website. """ if not should_export(): return # For backend logging in Supervisor. print(' - Exporting gas meter position to MinderGas.') # Just post the latest reading of the day before. today = timezone.localtime(timezone.now()) midnight = timezone.make_aware(timezone.datetime( year=today.year, month=today.month, day=today.day, hour=0, )) try: last_gas_reading = GasConsumption.objects.filter( # Slack of six hours to make sure we have any valid reading at all. read_at__range=(midnight - timezone.timedelta(hours=6), midnight) ).order_by('-read_at')[0] except IndexError: # Just continue, even though we have no data... yet. last_gas_reading = None if last_gas_reading: settings = MinderGasSettings.get_solo() # Register telegram by simply sending it to the application with a POST request. response = requests.post( MinderGasSettings.API_URL, headers={'Content-Type': 'application/json', 'AUTH-TOKEN': settings.auth_token}, data=json.dumps({ 'date': last_gas_reading.read_at.date().isoformat(), 'reading': str(last_gas_reading.delivered) }), ) if response.status_code != 201: raise AssertionError('MinderGas upload failed: %s (HTTP %s)'.format(response.text, response.status_code)) # Push back for a day. tomorrow = (today + timezone.timedelta(hours=24)).date() settings = MinderGasSettings.get_solo() settings.next_export = tomorrow settings.save()
def test_should_export_default(self, now_mock): """ Test should_export() default behaviour. """ now_mock.return_value = timezone.make_aware(timezone.datetime(2015, 12, 12, hour=0, minute=5)) settings = MinderGasSettings.get_solo() self.assertFalse(settings.export) self.assertIsNone(settings.auth_token) self.assertIsNone(settings.next_export) self.assertFalse(dsmr_mindergas.services.should_export())
def test_should_export_okay(self, now_mock): now_mock.return_value = timezone.make_aware(timezone.datetime(2015, 12, 12, hour=0, minute=5)) settings = MinderGasSettings.get_solo() settings.export = True settings.auth_token = "XXXXX" settings.save() self.assertTrue(dsmr_mindergas.services.should_export())
def export(): """ Exports gas readings to the MinderGas website. """ if not should_export(): return # Just post the latest reading of the day before. today = timezone.localtime(timezone.now()) midnight = timezone.make_aware(timezone.datetime( year=today.year, month=today.month, day=today.day, hour=0, )) try: last_gas_reading = GasConsumption.objects.filter( # Slack of six hours to make sure we have any valid reading at all. read_at__range=(midnight - timezone.timedelta(hours=6), midnight) ).order_by('-read_at')[0] except IndexError: # Just continue, even though we have no data... yet. last_gas_reading = None if last_gas_reading: settings = MinderGasSettings.get_solo() # Register telegram by simply sending it to the application with a POST request. response = requests.post( MinderGasSettings.API_URL, headers={'Content-Type': 'application/json', 'AUTH-TOKEN': settings.auth_token}, data=json.dumps({ 'date': last_gas_reading.read_at.date().isoformat(), 'reading': str(last_gas_reading.delivered) }), ) if response.status_code != 201: raise AssertionError('MinderGas upload failed: %s (HTTP %s)'.format(response.text, response.status_code)) # Push back for a day. tomorrow = (today + timezone.timedelta(hours=24)).date() settings = MinderGasSettings.get_solo() settings.next_export = tomorrow settings.save()
def test_should_export_default(self, now_mock): """ Test should_export() default behaviour. """ now_mock.return_value = timezone.make_aware( timezone.datetime(2015, 12, 12, hour=0, minute=5)) settings = MinderGasSettings.get_solo() self.assertFalse(settings.export) self.assertIsNone(settings.auth_token) self.assertIsNone(settings.next_export) self.assertFalse(dsmr_mindergas.services.should_export())
def test_should_export_okay(self, now_mock): now_mock.return_value = timezone.make_aware( timezone.datetime(2015, 12, 12, hour=0, minute=5)) settings = MinderGasSettings.get_solo() settings.export = True settings.auth_token = 'XXXXX' settings.save() self.assertTrue(dsmr_mindergas.services.should_export())
def test_export_fail(self, now_mock, should_export_mock, requests_post_mock): """ Test export() failing by denied API call. """ now_mock.return_value = timezone.make_aware(timezone.datetime(2015, 12, 12, hour=0, minute=5)) should_export_mock.return_value = True settings = MinderGasSettings.get_solo() self.assertFalse(settings.export) self.assertIsNone(settings.next_export) self.assertFalse(requests_post_mock.called) # Mindergas error codes according to docs. for current_error_code in (401, 422): requests_post_mock.return_value = mock.MagicMock(status_code=current_error_code, text="Error message") with self.assertRaises(AssertionError): dsmr_mindergas.services.export() settings = MinderGasSettings.get_solo() self.assertIsNone(settings.next_export) self.assertTrue(requests_post_mock.called)
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
def test_should_export_no_need(self, now_mock): """ Test should_export() when not needed, yet. """ now_mock.return_value = timezone.make_aware(timezone.datetime(2015, 12, 12, hour=0, minute=5)) tomorrow = (timezone.localtime(timezone.now()) + timezone.timedelta(hours=24)).date() settings = MinderGasSettings.get_solo() settings.export = True settings.auth_token = "XXXXX" settings.next_export = tomorrow # Cause of delay. settings.save() self.assertFalse(dsmr_mindergas.services.should_export())
def test_export_okay(self, now_mock, should_export_mock, requests_post_mock): """ Test export() as designed. """ now_mock.return_value = timezone.make_aware(timezone.datetime(2015, 12, 12, hour=0, minute=5)) should_export_mock.return_value = True requests_post_mock.return_value = mock.MagicMock(status_code=201, text="Fake accept") settings = MinderGasSettings.get_solo() self.assertFalse(settings.export) self.assertIsNone(settings.next_export) self.assertFalse(requests_post_mock.called) dsmr_mindergas.services.export() settings = MinderGasSettings.get_solo() self.assertIsNotNone(settings.next_export) self.assertTrue(requests_post_mock.called) # Check API parameters. requests_post_mock.assert_called_once_with( MinderGasSettings.API_URL, headers={"Content-Type": "application/json", "AUTH-TOKEN": settings.auth_token}, data=json.dumps({"date": "2015-12-11", "reading": "956.739"}), )
def test_should_export_no_gas(self, now_mock): """ Test should_export() without gas. """ now_mock.return_value = timezone.make_aware(timezone.datetime(2015, 12, 12, hour=0, minute=5)) settings = MinderGasSettings.get_solo() settings.export = True settings.auth_token = "XXXXX" settings.save() # Drop all gas data. GasConsumption.objects.all().delete() self.assertFalse(GasConsumption.objects.exists()) self.assertFalse(dsmr_mindergas.services.should_export())
def test_export_fail(self, now_mock, should_export_mock, requests_post_mock): """ Test export() failing by denied API call. """ now_mock.return_value = timezone.make_aware( timezone.datetime(2015, 12, 12, hour=0, minute=5)) should_export_mock.return_value = True settings = MinderGasSettings.get_solo() self.assertFalse(settings.export) self.assertIsNone(settings.next_export) self.assertFalse(requests_post_mock.called) # Mindergas error codes according to docs. for current_error_code in (401, 422): requests_post_mock.return_value = mock.MagicMock( status_code=current_error_code, text='Error message') with self.assertRaises(AssertionError): dsmr_mindergas.services.export() settings = MinderGasSettings.get_solo() self.assertIsNone(settings.next_export) self.assertTrue(requests_post_mock.called)
def test_should_export_no_need(self, now_mock): """ Test should_export() when not needed, yet. """ now_mock.return_value = timezone.make_aware( timezone.datetime(2015, 12, 12, hour=0, minute=5)) tomorrow = timezone.localtime( timezone.now()) + timezone.timedelta(hours=24) settings = MinderGasSettings.get_solo() settings.export = True settings.auth_token = 'XXXXX' settings.next_export = tomorrow # Cause of delay. settings.save() self.assertFalse(dsmr_mindergas.services.should_export())
def should_export(): """ Checks whether we should export data yet. Once every day. """ mindergas_settings = MinderGasSettings.get_solo() # Only when enabled and token set. if not mindergas_settings.export or not mindergas_settings.auth_token: return False # Nonsense when having no data. if not dsmr_backend.services.backend.get_capabilities(capability='gas'): return False return dsmr_backend.services.backend.is_timestamp_passed( timestamp=mindergas_settings.next_export)
def test_should_export_no_gas(self, now_mock): """ Test should_export() without gas. """ now_mock.return_value = timezone.make_aware( timezone.datetime(2015, 12, 12, hour=0, minute=5)) settings = MinderGasSettings.get_solo() settings.export = True settings.auth_token = 'XXXXX' settings.save() # Drop all gas data. GasConsumption.objects.all().delete() self.assertFalse(GasConsumption.objects.exists()) self.assertFalse(dsmr_mindergas.services.should_export())
def test_export_not_allowed(self, now_mock, should_export_mock): """ Test export() blocking behaviour. """ now_mock.return_value = timezone.make_aware(timezone.datetime(2015, 12, 12, hour=0, minute=5)) should_export_mock.return_value = False settings = MinderGasSettings.get_solo() self.assertFalse(settings.export) self.assertIsNone(settings.next_export) self.assertFalse(should_export_mock.called) # Nothing should happen. dsmr_mindergas.services.export() self.assertTrue(should_export_mock.called) self.assertIsNone(settings.next_export)
def test_export_not_allowed(self, now_mock, should_export_mock): """ Test export() blocking behaviour. """ now_mock.return_value = timezone.make_aware( timezone.datetime(2015, 12, 12, hour=0, minute=5)) should_export_mock.return_value = False settings = MinderGasSettings.get_solo() self.assertFalse(settings.export) self.assertIsNone(settings.next_export) self.assertFalse(should_export_mock.called) # Nothing should happen. dsmr_mindergas.services.export() self.assertTrue(should_export_mock.called) self.assertIsNone(settings.next_export)
def test_export_no_gas(self, now_mock, should_export_mock): """ Test export() without gas data. """ now_mock.return_value = timezone.make_aware(timezone.datetime(2015, 12, 12, hour=0, minute=5)) should_export_mock.return_value = True # Drop all gas data. GasConsumption.objects.all().delete() self.assertFalse(GasConsumption.objects.exists()) settings = MinderGasSettings.get_solo() self.assertFalse(settings.export) self.assertIsNone(settings.next_export) # Nothing should happen, as there is no data. dsmr_mindergas.services.export() self.assertIsNone(settings.next_export)
def test_export_no_gas(self, now_mock, should_export_mock): """ Test export() without gas data. """ now_mock.return_value = timezone.make_aware( timezone.datetime(2015, 12, 12, hour=0, minute=5)) should_export_mock.return_value = True # Drop all gas data. GasConsumption.objects.all().delete() self.assertFalse(GasConsumption.objects.exists()) settings = MinderGasSettings.get_solo() self.assertFalse(settings.export) self.assertIsNone(settings.next_export) # Nothing should happen, as there is no data. dsmr_mindergas.services.export() self.assertIsNone(settings.next_export)
def should_export(): """ Checks whether we should export data yet. Once every day. """ settings = MinderGasSettings.get_solo() # Only when enabled and token set. if not settings.export or not settings.auth_token: return False # Nonsense when having no data. capabilities = dsmr_backend.services.get_capabilities() if not capabilities['gas']: return False if settings.next_export is not None and timezone.localtime(timezone.now()).date() < settings.next_export: return False return True
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
def test_export_random_schedule(self, now_mock, should_export_mock, requests_post_mock): """ Test export() setting the next export randomly. """ now_mock.return_value = timezone.make_aware( timezone.datetime(2015, 12, 12, hour=0, minute=5)) should_export_mock.return_value = True requests_post_mock.return_value = mock.MagicMock(status_code=201, text='Fake accept') # We should at least have two different values (out of 10 attempts) random_values = [] for _ in range(0, 10): dsmr_mindergas.services.export() settings = MinderGasSettings.get_solo() self.assertIsNotNone(settings.next_export) random_values.append(settings.next_export) # Make unique and count them. random_values = list(set(random_values)) self.assertGreater(len(random_values), 2)
def setUp(self): self.mindergas_settings = MinderGasSettings.get_solo() self.mindergas_settings.update(export=True, auth_token='12345') self.schedule_process = ScheduledProcess.objects.get(module=settings.DSMRREADER_MODULE_MINDERGAS_EXPORT) self.schedule_process.update(active=True, planned=timezone.make_aware(timezone.datetime(2017, 1, 1)))