simo 2.5.31__py3-none-any.whl → 2.5.34__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of simo might be problematic. Click here for more details.

Files changed (35) hide show
  1. simo/core/__pycache__/autocomplete_views.cpython-38.pyc +0 -0
  2. simo/core/__pycache__/controllers.cpython-38.pyc +0 -0
  3. simo/core/__pycache__/tasks.cpython-38.pyc +0 -0
  4. simo/core/autocomplete_views.py +1 -1
  5. simo/core/controllers.py +1 -2
  6. simo/core/forms.py +2 -1
  7. simo/core/tasks.py +16 -1
  8. simo/core/utils/__pycache__/helpers.cpython-38.pyc +0 -0
  9. simo/fleet/__pycache__/forms.cpython-38.pyc +0 -0
  10. simo/fleet/__pycache__/socket_consumers.cpython-38.pyc +0 -0
  11. simo/fleet/forms.py +51 -19
  12. simo/fleet/migrations/0042_auto_20241120_1028.py +30 -0
  13. simo/fleet/migrations/__pycache__/0042_auto_20241120_1028.cpython-38.pyc +0 -0
  14. simo/fleet/socket_consumers.py +1 -0
  15. simo/fleet/tasks.py +2 -0
  16. simo/generic/scripting/helpers.py +23 -1
  17. simo/users/__pycache__/admin.cpython-38.pyc +0 -0
  18. simo/users/__pycache__/api.cpython-38.pyc +0 -0
  19. simo/users/__pycache__/models.cpython-38.pyc +0 -0
  20. simo/users/__pycache__/utils.cpython-38.pyc +0 -0
  21. simo/users/admin.py +2 -3
  22. simo/users/api.py +15 -16
  23. simo/users/migrations/0041_userdevicereportlog_speed_kmh_received.py +18 -0
  24. simo/users/migrations/0042_remove_userdevicereportlog_location_smoothed_and_more.py +21 -0
  25. simo/users/migrations/__pycache__/0041_userdevicereportlog_speed_kmh_received.cpython-38.pyc +0 -0
  26. simo/users/migrations/__pycache__/0042_remove_userdevicereportlog_location_smoothed_and_more.cpython-38.pyc +0 -0
  27. simo/users/models.py +0 -1
  28. simo/users/tasks.py +2 -0
  29. simo/users/utils.py +3 -100
  30. {simo-2.5.31.dist-info → simo-2.5.34.dist-info}/METADATA +1 -1
  31. {simo-2.5.31.dist-info → simo-2.5.34.dist-info}/RECORD +35 -29
  32. {simo-2.5.31.dist-info → simo-2.5.34.dist-info}/LICENSE.md +0 -0
  33. {simo-2.5.31.dist-info → simo-2.5.34.dist-info}/WHEEL +0 -0
  34. {simo-2.5.31.dist-info → simo-2.5.34.dist-info}/entry_points.txt +0 -0
  35. {simo-2.5.31.dist-info → simo-2.5.34.dist-info}/top_level.txt +0 -0
Binary file
@@ -117,7 +117,7 @@ class ComponentAutocomplete(autocomplete.Select2QuerySetView):
117
117
  )
118
118
 
119
119
  if self.q:
120
- qs = search_queryset(qs, self.q, ('name',))
120
+ qs = search_queryset(qs, self.q, ('zone__name', 'name',))
121
121
  return qs.distinct()
122
122
 
123
123
  def get_result_label(self, item):
simo/core/controllers.py CHANGED
@@ -505,8 +505,7 @@ class Button(ControllerBase):
505
505
  def is_held(self):
506
506
  return self.component.value == 'hold'
507
507
 
508
- @cached_property
509
- def bonded_gear(self):
508
+ def get_bonded_gear(self):
510
509
  from simo.core.models import Component
511
510
  gear = []
512
511
  for comp in Component.objects.filter(config__has_key='controls'):
simo/core/forms.py CHANGED
@@ -403,7 +403,8 @@ class NumericSensorForm(BaseComponentForm):
403
403
 
404
404
  def __init__(self, *args, **kwargs):
405
405
  super().__init__(*args, **kwargs)
406
- self.fields['value_units'].initial = self.controller.default_value_units
406
+ if 'value_units' in self.fields:
407
+ self.fields['value_units'].initial = self.controller.default_value_units
407
408
 
408
409
 
409
410
 
simo/core/tasks.py CHANGED
@@ -16,6 +16,7 @@ from django.utils import timezone
16
16
  from actstream.models import Action
17
17
  from simo.conf import dynamic_settings
18
18
  from simo.core.utils.helpers import get_self_ip
19
+ from simo.core.middleware import introduce_instance
19
20
  from simo.users.models import PermissionsRole, InstanceUser
20
21
  from .models import Instance, Component, ComponentHistory, HistoryAggregate
21
22
 
@@ -301,6 +302,7 @@ def sync_with_remote():
301
302
  def clear_history():
302
303
  for instance in Instance.objects.all():
303
304
  print(f"Clear history of {instance}")
305
+ introduce_instance(instance)
304
306
  old_times = timezone.now() - datetime.timedelta(
305
307
  days=instance.history_days
306
308
  )
@@ -434,14 +436,25 @@ def restart_postgresql():
434
436
  @celery_app.task
435
437
  def low_battery_notifications():
436
438
  from simo.notifications.utils import notify_users
439
+ from simo.generic.scripting.helpers import be_or_not_to_be
437
440
  for instance in Instance.objects.filter(is_active=True):
438
441
  timezone.activate(instance.timezone)
439
- if timezone.localtime().hour not in (10, 18):
442
+ hour = timezone.localtime().hour
443
+ if hour < 7:
440
444
  continue
445
+ if hour > 21:
446
+ continue
447
+
448
+ introduce_instance(instance)
441
449
  for comp in Component.objects.filter(
442
450
  zone__instance=instance,
443
451
  battery_level__isnull=False, battery_level__lt=20
444
452
  ):
453
+ last_warning = comp.meta.get('last_battery_warning', 0)
454
+ notify = be_or_not_to_be(12 * 60 * 60, 72 * 60 * 60, last_warning)
455
+ if not notify:
456
+ continue
457
+
445
458
  iusers = comp.zone.instance.instance_users.filter(
446
459
  is_active=True, role__is_owner=True
447
460
  )
@@ -451,6 +464,8 @@ def low_battery_notifications():
451
464
  f"Low battery ({comp.battery_level}%) on {comp}",
452
465
  component=comp, instance_users=iusers
453
466
  )
467
+ comp.meta['last_battery_warning'] = time.time()
468
+ comp.save()
454
469
 
455
470
 
456
471
  @celery_app.task
Binary file
simo/fleet/forms.py CHANGED
@@ -1192,7 +1192,10 @@ class BlindsConfigForm(ColonelComponentForm):
1192
1192
  )
1193
1193
 
1194
1194
  def __init__(self, *args, **kwargs):
1195
- self.basic_fields.append('retain_angle')
1195
+ self.basic_fields.extend(
1196
+ ['open_duration', 'close_duration',
1197
+ 'slats_angle_duration', 'retain_angle']
1198
+ )
1196
1199
  return super().__init__(*args, **kwargs)
1197
1200
 
1198
1201
  def clean(self):
@@ -1264,23 +1267,38 @@ class BlindsConfigForm(ColonelComponentForm):
1264
1267
 
1265
1268
 
1266
1269
  class GateConfigForm(ColonelComponentForm):
1267
- control_pin = Select2ModelChoiceField(
1268
- label="Control Relay Port",
1270
+ open_pin = Select2ModelChoiceField(
1271
+ label="Open Relay Port",
1269
1272
  queryset=ColonelPin.objects.filter(output=True),
1270
1273
  url='autocomplete-colonel-pins',
1271
1274
  forward=[
1272
1275
  forward.Self(),
1273
1276
  forward.Field('colonel'),
1274
1277
  forward.Const({'output': True}, 'filters')
1275
- ]
1278
+ ], help_text="If your gate is controlled by single input, "
1279
+ "using this port is enough."
1276
1280
  )
1277
1281
  open_action = forms.ChoiceField(
1278
- choices=(('HIGH', "HIGH"), ('LOW', "LOW")), initial='HIGH'
1282
+ choices=(('HIGH', "HIGH"), ('LOW', "LOW")),
1283
+ )
1284
+ close_pin = Select2ModelChoiceField(
1285
+ label="Close Relay Port",
1286
+ queryset=ColonelPin.objects.filter(output=True),
1287
+ url='autocomplete-colonel-pins',
1288
+ forward=[
1289
+ forward.Self(),
1290
+ forward.Field('colonel'),
1291
+ forward.Const({'output': True}, 'filters')
1292
+ ], required=False
1293
+ )
1294
+ close_action = forms.ChoiceField(
1295
+ choices=(('HIGH', "HIGH"), ('LOW', "LOW")),
1279
1296
  )
1280
1297
  control_method = forms.ChoiceField(
1281
1298
  choices=(('pulse', "Pulse"), ('hold', "Hold")), initial="pulse",
1282
- help_text="What your gate motor expects to receive as control command?"
1299
+ help_text="What your gate motors expect to receive as control command?"
1283
1300
  )
1301
+
1284
1302
  sensor_pin = Select2ModelChoiceField(
1285
1303
  label='Gate open/closed sensor port',
1286
1304
  queryset=ColonelPin.objects.filter(input=True),
@@ -1289,7 +1307,7 @@ class GateConfigForm(ColonelComponentForm):
1289
1307
  forward.Self(),
1290
1308
  forward.Field('colonel'),
1291
1309
  forward.Const({'input': True}, 'filters')
1292
- ]
1310
+ ], required=False,
1293
1311
  )
1294
1312
  closed_value = forms.ChoiceField(
1295
1313
  label='Gate closed value',
@@ -1297,6 +1315,8 @@ class GateConfigForm(ColonelComponentForm):
1297
1315
  help_text="What is the input sensor value, "
1298
1316
  "when your gate is in closed position?"
1299
1317
  )
1318
+
1319
+
1300
1320
  open_duration = forms.FloatField(
1301
1321
  initial=30, min_value=1, max_value=600,
1302
1322
  help_text="How much time in seconds does it take for your gate "
@@ -1312,15 +1332,24 @@ class GateConfigForm(ColonelComponentForm):
1312
1332
  def clean(self):
1313
1333
  super().clean()
1314
1334
 
1315
- if self.cleaned_data.get('control_pin') \
1316
- and self.cleaned_data.get('sensor_pin') \
1317
- and self.cleaned_data['control_pin'] == self.cleaned_data['sensor_pin']:
1318
- self.add_error(
1319
- 'sensor_pin', "Can't be the same as control port!"
1320
- )
1335
+ check_pins = ('open_pin', 'close_pin', 'sensor_pin')
1336
+ for pin in check_pins:
1337
+ if not self.cleaned_data.get(pin):
1338
+ continue
1339
+ for p in check_pins:
1340
+ if pin == pin:
1341
+ continue
1342
+ if not self.cleaned_data.get(p):
1343
+ continue
1344
+ if self.cleaned_data[pin] == self.cleaned_data[p]:
1345
+ self.add_error(
1346
+ pin, f"Can't be the same {p}!"
1347
+ )
1321
1348
 
1322
- if self.cleaned_data.get('control_pin'):
1323
- self._clean_pin('control_pin')
1349
+ if self.cleaned_data.get('open_pin'):
1350
+ self._clean_pin('open_pin')
1351
+ if self.cleaned_data.get('close_pin'):
1352
+ self._clean_pin('close_pin')
1324
1353
  if self.cleaned_data.get('sensor_pin'):
1325
1354
  self._clean_pin('sensor_pin')
1326
1355
 
@@ -1350,10 +1379,13 @@ class GateConfigForm(ColonelComponentForm):
1350
1379
  return self.cleaned_data
1351
1380
 
1352
1381
  def save(self, commit=True):
1353
- if 'control_pin' in self.cleaned_data:
1354
- self.instance.config['control_pin_no'] = \
1355
- self.cleaned_data['control_pin'].no
1356
- if 'sensor_pin' in self.cleaned_data:
1382
+ if self.cleaned_data.get('open_pin'):
1383
+ self.instance.config['open_pin_no'] = \
1384
+ self.cleaned_data['open_pin'].no
1385
+ if self.cleaned_data.get('close_pin'):
1386
+ self.instance.config['close_pin_no'] = \
1387
+ self.cleaned_data['close_pin'].no
1388
+ if self.cleaned_data.get('sensor_pin'):
1357
1389
  self.instance.config['sensor_pin_no'] = \
1358
1390
  self.cleaned_data['sensor_pin'].no
1359
1391
  obj = super().save(commit=commit)
@@ -0,0 +1,30 @@
1
+ # Generated by Django 4.2.10 on 2024-11-20 10:28
2
+
3
+ from django.db import migrations
4
+
5
+
6
+ def forwards_func(apps, schema_editor):
7
+ Component = apps.get_model("core", "Component")
8
+
9
+ for gate in Component.objects.filter(
10
+ controller_uid='simo.fleet.controllers.Gate'
11
+ ):
12
+ gate.config['open_pin_no'] = gate.config.get('control_pin_no')
13
+ gate.config['open_pin'] = gate.config.get('control_pin')
14
+ gate.save()
15
+
16
+
17
+ def reverse_func(apps, schema_editor):
18
+ pass
19
+
20
+
21
+
22
+ class Migration(migrations.Migration):
23
+
24
+ dependencies = [
25
+ ('fleet', '0041_alter_colonel_instance_and_more'),
26
+ ]
27
+
28
+ operations = [
29
+ migrations.RunPython(forwards_func, reverse_func, elidable=True),
30
+ ]
@@ -363,6 +363,7 @@ class FleetConsumer(AsyncWebsocketConsumer):
363
363
 
364
364
 
365
365
  async def receive(self, text_data=None, bytes_data=None):
366
+ drop_current_instance()
366
367
  try:
367
368
  if text_data:
368
369
  print(f"{self.colonel}: {text_data}")
simo/fleet/tasks.py CHANGED
@@ -1,6 +1,7 @@
1
1
  import datetime
2
2
  from django.db.models import Prefetch
3
3
  from django.utils import timezone
4
+ from simo.core.middleware import drop_current_instance
4
5
  from celeryc import celery_app
5
6
 
6
7
 
@@ -8,6 +9,7 @@ from celeryc import celery_app
8
9
  def check_colonel_components_alive():
9
10
  from simo.core.models import Component
10
11
  from .models import Colonel
12
+ drop_current_instance()
11
13
  for lost_colonel in Colonel.objects.filter(
12
14
  last_seen__lt=timezone.now() - datetime.timedelta(seconds=60)
13
15
  ).prefetch_related(Prefetch(
@@ -1,5 +1,7 @@
1
1
  import pytz
2
2
  import math
3
+ import time
4
+ import random
3
5
  from django.utils import timezone
4
6
  from suntime import Sun
5
7
  from simo.core.models import Instance
@@ -88,4 +90,24 @@ def haversine_distance(location1, location2, units_of_measure='metric'):
88
90
  else:
89
91
  distance = distance_meters # Keep in meters for 'metric'
90
92
 
91
- return distance
93
+ return distance
94
+
95
+
96
+ def be_or_not_to_be(min_seconds, max_seconds, last_be_timestamp=0):
97
+ '''
98
+ Returns True if max_hours has passed after last_be or last_be is not provided
99
+ Returns False if min_hours not yet passed
100
+ Returns True or False if if last_be is in betwen of min_hours and max_hours
101
+ with rising probability of Trye from 0% (min_hours) to 100% (max_hours)
102
+ '''
103
+ if last_be_timestamp:
104
+ seconds_since_last = time.time() - last_be_timestamp
105
+ else:
106
+ seconds_since_last = max_seconds
107
+
108
+ if seconds_since_last >= max_seconds:
109
+ return True
110
+ if seconds_since_last >= min_seconds: # Calculate probability after min_hours hours
111
+ probability = min((seconds_since_last - min_seconds) / (max_seconds - min_seconds), 1.0)
112
+ return random.random() < probability
113
+ return False
Binary file
Binary file
Binary file
simo/users/admin.py CHANGED
@@ -132,12 +132,11 @@ class UserDeviceLog(admin.ModelAdmin):
132
132
  model = UserDeviceReportLog
133
133
  readonly_fields = (
134
134
  'timestamp', 'app_open', 'relay', 'at_home',
135
- 'location', 'location_smoothed', 'users',
136
- 'speed_kmh', 'phone_on_charge'
135
+ 'location', 'users', 'speed_kmh', 'phone_on_charge'
137
136
  )
138
137
  list_display = (
139
138
  'timestamp', 'app_open', 'relay', 'at_home',
140
- 'location', 'location_smoothed', 'speed_kmh',
139
+ 'location', 'speed_kmh',
141
140
  'phone_on_charge', 'users'
142
141
  )
143
142
  fields = readonly_fields
simo/users/api.py CHANGED
@@ -11,7 +11,6 @@ from django.utils import timezone
11
11
  from django_filters.rest_framework import DjangoFilterBackend
12
12
  from simo.conf import dynamic_settings
13
13
  from simo.core.api import InstanceMixin
14
- from simo.users.utils import get_smoothed_location
15
14
  from .models import (
16
15
  User, UserDevice, UserDeviceReportLog, PermissionsRole, InstanceInvitation,
17
16
  Fingerprint, ComponentPermission, InstanceUser
@@ -197,20 +196,21 @@ class UserDeviceReport(InstanceMixin, viewsets.GenericViewSet):
197
196
  if request.META.get('HTTP_HOST', '').endswith('.simo.io'):
198
197
  relay = request.META.get('HTTP_HOST')
199
198
 
199
+ try:
200
+ speed_kmh = request.data.get('speed', 0) * 3.6
201
+ except:
202
+ speed_kmh = 0
200
203
 
201
204
  if relay:
202
205
  location = request.data.get('location')
203
- if location:
204
- location_smoothed = get_smoothed_location(user_device, location)
205
- else:
206
- location_smoothed = None
206
+ if 'null' in location:
207
+ location = None
207
208
  else:
208
209
  location = self.instance.location
209
- location_smoothed = location
210
210
 
211
211
  user_device.last_seen = timezone.now()
212
212
 
213
- if request.data.get('app_open', False):
213
+ if request.data.get('app_open', False) == True:
214
214
  user_device.is_primary = True
215
215
  UserDevice.objects.filter(
216
216
  users=request.user
@@ -218,28 +218,28 @@ class UserDeviceReport(InstanceMixin, viewsets.GenericViewSet):
218
218
  user_device.save()
219
219
 
220
220
  phone_on_charge = False
221
- if request.data.get('is_charging'):
221
+ if request.data.get('is_charging') == True:
222
222
  phone_on_charge = True
223
- speed_kmh = request.data.get('speed', 0) * 3.6
223
+
224
224
 
225
225
  at_home = False
226
226
  if not relay:
227
227
  at_home = True
228
- elif location_smoothed:
228
+ elif location:
229
229
  at_home = haversine_distance(
230
- self.instance.location, location_smoothed
230
+ self.instance.location, location
231
231
  ) < dynamic_settings['users__at_home_radius']
232
232
 
233
233
  for iu in request.user.instance_roles.filter(is_active=True):
234
234
  if not relay:
235
235
  iu.at_home = True
236
- elif location_smoothed:
236
+ elif location:
237
237
  iu.at_home = haversine_distance(
238
- iu.instance.location, location_smoothed
238
+ iu.instance.location, location
239
239
  ) < dynamic_settings['users__at_home_radius']
240
240
 
241
241
  iu.last_seen = user_device.last_seen
242
- iu.last_seen_location = location_smoothed
242
+ iu.last_seen_location = location
243
243
  iu.last_seen_speed_kmh = speed_kmh
244
244
  iu.phone_on_charge = phone_on_charge
245
245
  iu.save()
@@ -253,8 +253,7 @@ class UserDeviceReport(InstanceMixin, viewsets.GenericViewSet):
253
253
  UserDeviceReportLog.objects.create(
254
254
  user_device=user_device, instance=self.instance,
255
255
  app_open=request.data.get('app_open', False),
256
- location=location, location_smoothed=location_smoothed,
257
- datetime=log_datetime,
256
+ location=location, datetime=log_datetime,
258
257
  relay=relay, speed_kmh=speed_kmh,
259
258
  phone_on_charge=phone_on_charge, at_home=at_home
260
259
  )
@@ -0,0 +1,18 @@
1
+ # Generated by Django 4.2.10 on 2024-11-19 07:54
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ('users', '0040_userdevicereportlog_location_smoothed_and_more'),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AddField(
14
+ model_name='userdevicereportlog',
15
+ name='speed_kmh_received',
16
+ field=models.FloatField(default=0),
17
+ ),
18
+ ]
@@ -0,0 +1,21 @@
1
+ # Generated by Django 4.2.10 on 2024-11-19 12:27
2
+
3
+ from django.db import migrations
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ('users', '0041_userdevicereportlog_speed_kmh_received'),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.RemoveField(
14
+ model_name='userdevicereportlog',
15
+ name='location_smoothed',
16
+ ),
17
+ migrations.RemoveField(
18
+ model_name='userdevicereportlog',
19
+ name='speed_kmh_received',
20
+ ),
21
+ ]
simo/users/models.py CHANGED
@@ -447,7 +447,6 @@ class UserDeviceReportLog(models.Model):
447
447
  help_text="Sent via remote relay if specified, otherwise it's from LAN."
448
448
  )
449
449
  location = PlainLocationField(zoom=7, null=True, blank=True)
450
- location_smoothed = PlainLocationField(zoom=7, null=True, blank=True)
451
450
  speed_kmh = models.FloatField(default=0)
452
451
  phone_on_charge = models.BooleanField(default=False, db_index=True)
453
452
  at_home = models.BooleanField(default=True)
simo/users/tasks.py CHANGED
@@ -1,5 +1,6 @@
1
1
  import datetime
2
2
  from django.utils import timezone
3
+ from simo.core.middleware import introduce_instance
3
4
  from celeryc import celery_app
4
5
 
5
6
 
@@ -8,6 +9,7 @@ def clear_device_report_logs():
8
9
  from simo.core.models import Instance
9
10
  from .models import UserDeviceReportLog
10
11
  for instance in Instance.objects.all():
12
+ introduce_instance(instance)
11
13
  # keeping at least 1 hour of logs so that we could evaluate
12
14
  # user's current location using Kalman filter
13
15
  UserDeviceReportLog.objects.filter(
simo/users/utils.py CHANGED
@@ -1,4 +1,5 @@
1
1
  import sys
2
+ import math
2
3
  import traceback
3
4
  import subprocess
4
5
  import datetime
@@ -8,6 +9,7 @@ from django.utils import timezone
8
9
  from django.template.loader import render_to_string
9
10
 
10
11
 
12
+
11
13
  def get_system_user():
12
14
  from .models import User
13
15
  system, new = User.objects.get_or_create(
@@ -62,103 +64,4 @@ def update_mqtt_acls():
62
64
  )
63
65
  subprocess.run(
64
66
  ['service', 'mosquitto', 'reload'], stdout=subprocess.PIPE
65
- )
66
-
67
-
68
- class KalmanFilter:
69
- def __init__(self, process_variance, measurement_variance, x=None, P=None):
70
- self.x = x if x is not None else np.array([[0], [0], [0], [0]]) # State
71
- self.P = P if P is not None else np.eye(4) # State covariance
72
- self.process_variance = process_variance
73
- self.measurement_variance = measurement_variance
74
-
75
- def predict(self, delta_t):
76
- F = np.array([
77
- [1, 0, delta_t, 0],
78
- [0, 1, 0, delta_t],
79
- [0, 0, 1, 0],
80
- [0, 0, 0, 1]
81
- ])
82
- Q = self.process_variance * np.array([
83
- [delta_t ** 4 / 4, 0, delta_t ** 3 / 2, 0],
84
- [0, delta_t ** 4 / 4, 0, delta_t ** 3 / 2],
85
- [delta_t ** 3 / 2, 0, delta_t ** 2, 0],
86
- [0, delta_t ** 3 / 2, 0, delta_t ** 2]
87
- ])
88
- self.x = np.dot(F, self.x)
89
- self.P = np.dot(np.dot(F, self.P), F.T) + Q
90
-
91
- def update(self, z):
92
- H = np.array([
93
- [1, 0, 0, 0],
94
- [0, 1, 0, 0]
95
- ])
96
- R = self.measurement_variance * np.eye(2)
97
- y = z - np.dot(H, self.x) # Innovation
98
- S = np.dot(H, np.dot(self.P, H.T)) + R # Innovation covariance
99
- K = np.dot(np.dot(self.P, H.T), np.linalg.inv(S)) # Kalman Gain
100
- self.x = self.x + np.dot(K, y)
101
- I = np.eye(self.P.shape[0])
102
- self.P = np.dot(I - np.dot(K, H), self.P)
103
-
104
- def get_state(self):
105
- return self.x[:2].flatten() # Latitude and Longitude
106
-
107
-
108
- def get_smoothed_location(user_device, new_location):
109
- try:
110
- new_lat, new_lon = map(float, new_location.split(','))
111
- except ValueError:
112
- raise ValueError("Invalid new location format. Expected 'lat,lon'.")
113
-
114
- cache_key = f"kalman_state_{user_device.id}"
115
- cached_data = cache.get(cache_key)
116
-
117
- if cached_data:
118
- kf = KalmanFilter(
119
- process_variance=1,
120
- measurement_variance=10,
121
- x=np.array(cached_data['x']),
122
- P=np.array(cached_data['P'])
123
- )
124
- last_processed_time = cached_data['last_processed_time']
125
- else:
126
- kf = KalmanFilter(process_variance=1, measurement_variance=10)
127
- last_processed_time = None
128
-
129
- last_log = None
130
- logs_query = user_device.report_logs.filter(
131
- location__isnull=False,
132
- datetime__gt=last_processed_time or timezone.now() - datetime.timedelta(minutes=20)
133
- ).order_by('datetime')
134
-
135
- for log in logs_query:
136
- try:
137
- lat, lon = map(float, log.location.split(','))
138
- except ValueError:
139
- continue # Skip invalid data
140
-
141
- if last_log and log.location == last_log.location:
142
- continue # Skip duplicate locations
143
-
144
- if last_log:
145
- delta_t = (log.datetime - last_log.datetime).total_seconds()
146
- kf.predict(max(delta_t, 0)) # Prevent negative delta_t
147
-
148
- kf.update(np.array([[lat], [lon]]))
149
- last_log = log
150
-
151
- if last_log:
152
- delta_t = (timezone.now() - last_log.datetime).total_seconds()
153
- kf.predict(max(delta_t, 0))
154
-
155
- kf.update(np.array([[new_lat], [new_lon]]))
156
-
157
- # Cache the updated filter state and last processed log time
158
- cache.set(cache_key, {
159
- 'x': kf.x.tolist(), # Convert to list for JSON serialization
160
- 'P': kf.P.tolist(), # Convert to list for JSON serialization
161
- 'last_processed_time': timezone.now()
162
- }, timeout=3600) # Cache for 1 hour
163
-
164
- return ','.join(f"{coord:.6f}" for coord in kf.get_state())
67
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: simo
3
- Version: 2.5.31
3
+ Version: 2.5.34
4
4
  Summary: Smart Home on Steroids!
5
5
  Author-email: Simanas Venčkauskas <simanas@simo.io>
6
6
  Project-URL: Homepage, https://simo.io
@@ -39,15 +39,15 @@ simo/core/api_meta.py,sha256=EaiY-dCADP__9MvLpoHvhjytFT92IrxPZDv95xgqasU,4955
39
39
  simo/core/app_widgets.py,sha256=VxZzapuc-a29wBH7JzpvNF2SK1ECrgNUySId5ke1ffc,2509
40
40
  simo/core/apps.py,sha256=CsqpiQerhmrMsH-wGiG-gQgXd9qEkIi-LUaA9cXpKSw,425
41
41
  simo/core/auto_urls.py,sha256=nNXEgLAAAQAhRWQDA9AbDtw-zcPKmu_pufJaSa8g818,1102
42
- simo/core/autocomplete_views.py,sha256=l7DmpoeXcwjmBda--tT6zFQTNcyfYpgTaryWP-ghETU,3846
42
+ simo/core/autocomplete_views.py,sha256=Nu9sgc0X5OspRkJnpomeO2XdHCOg9ii9v4ZkGecCNjQ,3860
43
43
  simo/core/base_types.py,sha256=WypW8hTfzveuTQtruGjLYAGQZIuczxTlW-SdRk3iQug,666
44
44
  simo/core/context.py,sha256=LKw1I4iIRnlnzoTCuSLLqDX7crHdBnMo3hjqYvVmzFc,1557
45
- simo/core/controllers.py,sha256=JvXdwXD7iotA7fIjZmBVshKLQGSt9Na48FMAyHRDh84,35615
45
+ simo/core/controllers.py,sha256=Oy89sRh11eHlyPyrvmgxhK7kc7Aw9z0ndVMMyHhwRyM,35598
46
46
  simo/core/dynamic_settings.py,sha256=bUs58XEZOCIEhg1TigR3LmYggli13KMryBZ9pC7ugAQ,1872
47
47
  simo/core/events.py,sha256=1_KIk5pJqdLPRQlCQ9xSyALst2Cn0b2lAEAJ3QjwIjE,4801
48
48
  simo/core/filters.py,sha256=ghtOZcrwNAkIyF5_G9Sn73NkiI71mXv0NhwCk4IyMIM,411
49
49
  simo/core/form_fields.py,sha256=wowWocYgxkKBr0WYzpKn4UvH4ScnImus56Tg2G8OPBc,2274
50
- simo/core/forms.py,sha256=O40apPH7a4qX4WdCc10A1aoAaGWOpKqmjB8d-OhEKCo,21523
50
+ simo/core/forms.py,sha256=cLsiyTIAlVni_ir0VplPs6hCdx8SHDkdNW4I9C-lhyw,21568
51
51
  simo/core/gateways.py,sha256=m0eS3XjVe34Dge6xtoCq16kFWCKJcdQrT0JW0REqoq8,3715
52
52
  simo/core/loggers.py,sha256=EBdq23gTQScVfQVH-xeP90-wII2DQFDjoROAW6ggUP4,1645
53
53
  simo/core/managers.py,sha256=n-b3I4uXzfHKTeB1VMjSaMsDUxp8FegFJwnbV1IsWQ4,3019
@@ -59,7 +59,7 @@ simo/core/serializers.py,sha256=WgksN1Ombv240nfQR_UtmKslTWM9vz9Y0yTdN5usiHU,2189
59
59
  simo/core/signal_receivers.py,sha256=2WfF3FI5ZmJM1S-oT_1w3TdnBUX6fjbI4Rpg-DKsuYA,8696
60
60
  simo/core/socket_consumers.py,sha256=trRZvBGTJ7xIbfdmVvn7zoiWp_qssSkMZykDrI5YQyE,9783
61
61
  simo/core/storage.py,sha256=_5igjaoWZAiExGWFEJMElxUw55DzJG1jqFty33xe8BE,342
62
- simo/core/tasks.py,sha256=yrZORvMv7imfsyLIPn8TGDwo7oN7SDY1Uxv4C67Unfo,16406
62
+ simo/core/tasks.py,sha256=MnyEFDzdIGpE0nvR8BOksSybcKYKj2j8QpyzmX4TXd8,16931
63
63
  simo/core/todos.py,sha256=eYVXfLGiapkxKK57XuviSNe3WsUYyIWZ0hgQJk7ThKo,665
64
64
  simo/core/types.py,sha256=WJEq48mIbFi_5Alt4wxWMGXxNxUTXqfQU5koH7wqHHI,1108
65
65
  simo/core/views.py,sha256=3SRZr00fyLQf8ja3U-9eekKt-ld5TvU1WQqUWprXfQ4,2390
@@ -72,10 +72,10 @@ simo/core/__pycache__/api_meta.cpython-38.pyc,sha256=VYx5ZeDyNBI4B_CBEIhV5B3GnLs
72
72
  simo/core/__pycache__/app_widgets.cpython-38.pyc,sha256=oN657XMMZ6GYN9nblv7fX3kdnTEzSP9XV6PXM6Z0wl4,4358
73
73
  simo/core/__pycache__/apps.cpython-38.pyc,sha256=JL0BEqgXcSQvMlcK48PBpPfyDEkPMdO1Y0teqMRGirs,713
74
74
  simo/core/__pycache__/auto_urls.cpython-38.pyc,sha256=Tyf8PYHq5YqSwTp25Joy-eura_Fm86fpX9zKLSklhvo,872
75
- simo/core/__pycache__/autocomplete_views.cpython-38.pyc,sha256=YoF9mTXfQtZ0ZC4wIVvYe4RUUnfV8OlCkIDTVT0LwBw,3953
75
+ simo/core/__pycache__/autocomplete_views.cpython-38.pyc,sha256=Of2ZFdN8XF1I4TmAUwqZonHqZd8O9CpX_DqIQMU_rdc,3967
76
76
  simo/core/__pycache__/base_types.cpython-38.pyc,sha256=CX-qlF7CefRi_mCE954wYa9rUFR88mOl6g7fybDRu7g,803
77
77
  simo/core/__pycache__/context.cpython-38.pyc,sha256=NlTHt2GvXxA21AhBkeyOLfRFUuXw7wmwqyNhhcDl2cw,1373
78
- simo/core/__pycache__/controllers.cpython-38.pyc,sha256=6Ts1BeGND9Uy5eeqC5dNeme1yYilEV_emRnjTjJ9WNw,30701
78
+ simo/core/__pycache__/controllers.cpython-38.pyc,sha256=brd76tiiaoOV_DPKKMA6ZBGD-GBg4wc3VKpuxunvwBk,30698
79
79
  simo/core/__pycache__/dynamic_settings.cpython-38.pyc,sha256=wGpnscX1DxFpRl54MQURhjz2aD3NJohSzw9JCFnzh2Y,2384
80
80
  simo/core/__pycache__/events.cpython-38.pyc,sha256=1y8YaZsiDkBOeIWzH7SQz4holmMG_RLlMWi8kuSZcoE,5280
81
81
  simo/core/__pycache__/filters.cpython-38.pyc,sha256=VIMADCBiYhziIyRmxAyUDJluZvuZmiC4bNYWTRsGSao,721
@@ -92,7 +92,7 @@ simo/core/__pycache__/serializers.cpython-38.pyc,sha256=d4wpUjFuo8GxaNWbin9GdHKi
92
92
  simo/core/__pycache__/signal_receivers.cpython-38.pyc,sha256=Od1ejof2nHAFzTAG5vGGVjMA8WUJNVJ9o_KWGqRSR34,6669
93
93
  simo/core/__pycache__/socket_consumers.cpython-38.pyc,sha256=KqbO1cOewodVPcy0-htVefyUjCuELKV0o7fOfYqfgPc,8490
94
94
  simo/core/__pycache__/storage.cpython-38.pyc,sha256=9R1Xu0FJDflfRXUPsqEgt0SpwiP7FGk7HaR8s8XRyI8,721
95
- simo/core/__pycache__/tasks.cpython-38.pyc,sha256=0cdIMWxwx8qkeseOvZcgIZ_BXrGbkhfijRUfpIa3s8s,10896
95
+ simo/core/__pycache__/tasks.cpython-38.pyc,sha256=2LjlK_AA7b8_X-bnmAtdtxBqblyOCS8iytjwx_q8dt4,11228
96
96
  simo/core/__pycache__/todos.cpython-38.pyc,sha256=lOqGZ58siHM3isoJV4r7sg8igrfE9fFd-jSfeBa0AQI,253
97
97
  simo/core/__pycache__/views.cpython-38.pyc,sha256=K_QM967bIJeU02DJu0Dm7j8RiFDKn_TLzX77YzNkA7c,2495
98
98
  simo/core/__pycache__/widgets.cpython-38.pyc,sha256=sR0ZeHCHrhnNDBJuRrxp3zUsfBp0xrtF0xrK2TkQv1o,3520
@@ -10211,7 +10211,7 @@ simo/core/utils/__pycache__/easing.cpython-38.pyc,sha256=LupxHv19OiBqL0VXmTbMCtA
10211
10211
  simo/core/utils/__pycache__/form_fields.cpython-38.pyc,sha256=nBk6k9aj6BpWwdkpceIXdl5NU0fB6NPFhSBPaA-VtPs,1252
10212
10212
  simo/core/utils/__pycache__/form_widgets.cpython-38.pyc,sha256=MYAYEq0I4P0WErG9FamTJYWue7-cPartAWbFAiSSg5w,908
10213
10213
  simo/core/utils/__pycache__/formsets.cpython-38.pyc,sha256=b0BuoiXFQwNgok0OWuMma8pwTC6s9c-s9yuet_YLlZk,4946
10214
- simo/core/utils/__pycache__/helpers.cpython-38.pyc,sha256=jTGaN7kSJRwouP0EuYSaiJeMylo_RzJwSm-DKRwceHA,4291
10214
+ simo/core/utils/__pycache__/helpers.cpython-38.pyc,sha256=-lTIUJxhV03q8P9ZGcIhy6vftS7s29m_MpKt3bKefMY,4291
10215
10215
  simo/core/utils/__pycache__/json.cpython-38.pyc,sha256=TKc88VpPKgimeaozhgx34WWJ1mwTWFWN6B9-VAH8qT0,532
10216
10216
  simo/core/utils/__pycache__/logs.cpython-38.pyc,sha256=BVVeQoOhfRHm3SHnCoE1d5G84kTpJZFmr_btc3jDYTU,2156
10217
10217
  simo/core/utils/__pycache__/mixins.cpython-38.pyc,sha256=8Js2T7jVQ7hugRUIRu3rdxW86dJW4KeUUWqKqSkIGb0,615
@@ -10228,14 +10228,14 @@ simo/fleet/auto_urls.py,sha256=UX66eR2ykMqFgfIllW-RTdjup5-FieCWl_BVm3CcXKg,702
10228
10228
  simo/fleet/base_types.py,sha256=wL9RVkHr0gA7HI1wZq0pruGEIgvQqpfnCL4cC3ywsvw,102
10229
10229
  simo/fleet/ble.py,sha256=eHA_9ABjbmH1vUVCv9hiPXQL2GZZSEVwfO0xyI1S0nI,1081
10230
10230
  simo/fleet/controllers.py,sha256=fjri1GtCnflkkDpNqhTwy6i9CK6RDEB0Q_BtADzcG8E,29156
10231
- simo/fleet/forms.py,sha256=dNyM0hAKR93DPeKs6NJnE42prAeCR4MoXjkv5mGeQ4g,62952
10231
+ simo/fleet/forms.py,sha256=Z2iTfV_3Ui2QS2mQX7vPtLqiHvHprwkVSeyJ45c0aeU,64047
10232
10232
  simo/fleet/gateways.py,sha256=lKEJW0MgaOEiNnijH50DNSVChvaUT3TA3UurcI57P8k,5677
10233
10233
  simo/fleet/managers.py,sha256=ZNeHFSkF5kzsl9E1DCBevOW6kXJlD6kw0LU4B-JMOG8,828
10234
10234
  simo/fleet/models.py,sha256=zPplx_v64nfKBmb-nCb74aCVtEeY3m3SjEy-VhbnydU,17511
10235
10235
  simo/fleet/routing.py,sha256=cofGsVWXMfPDwsJ6HM88xxtRxHwERhJ48Xyxc8mxg5o,149
10236
10236
  simo/fleet/serializers.py,sha256=X2M0DFKVaxM6JFGDsdg3S2nJlLIcBvbujidZdfxD88w,2169
10237
- simo/fleet/socket_consumers.py,sha256=PwxGOqstI5wzglBHTvxosa9mwg8n3tWDQEPLb1vbxlc,18354
10238
- simo/fleet/tasks.py,sha256=AGq9BXFNAqkhOANsPvId8yjEbDtVCB3MRsi_AKDpgIM,821
10237
+ simo/fleet/socket_consumers.py,sha256=0OU4lQHUBWB8VU2-Iqkjmj4eWqGkGDg0RFQhBIshRKE,18386
10238
+ simo/fleet/tasks.py,sha256=NX_opj-rbkK9eeQMeRNwHj_ZJ0LC4rYOZovMg9_r0kA,904
10239
10239
  simo/fleet/utils.py,sha256=wNJvURzLP3-aho3D3rfg07N9kWCaMIw5gOsmeeO9Nlg,4740
10240
10240
  simo/fleet/views.py,sha256=jT3GcGv_JEj3dqyfHH2whCnGqwT8YEAuFxRgIX4Dk9w,3237
10241
10241
  simo/fleet/__pycache__/__init__.cpython-38.pyc,sha256=pIZE7EL6-cuJ3pQtaSwjKLrKLsTYelp1k9sRhXKLh6s,159
@@ -10245,13 +10245,13 @@ simo/fleet/__pycache__/auto_urls.cpython-38.pyc,sha256=Tc6a6BCXHjijP8U2jE2ghlJwn
10245
10245
  simo/fleet/__pycache__/base_types.cpython-38.pyc,sha256=deyPwjpT6xZiFxBGFnj5b7R-lbdOTh2krgpJhrcGVhc,274
10246
10246
  simo/fleet/__pycache__/ble.cpython-38.pyc,sha256=Nrof9w7cm4OlpFWHeVnmvvanh2_oF9oQ3TknJiV93-0,1267
10247
10247
  simo/fleet/__pycache__/controllers.cpython-38.pyc,sha256=jtFHr_uyjCCeuidL-o-hGaf21u0fnxK_O6hTRdY6lpc,24906
10248
- simo/fleet/__pycache__/forms.cpython-38.pyc,sha256=09TcBfKorvkokHkLRua0gg8sVBamwjEhvCpoANRhPyg,42595
10248
+ simo/fleet/__pycache__/forms.cpython-38.pyc,sha256=asyDqRo5kRIaFhWqDRXqBPxKGl43O6O00dXOemUN6p0,43057
10249
10249
  simo/fleet/__pycache__/gateways.cpython-38.pyc,sha256=0RKVn0ndreVKhsrukqeLPSdMnRrsQ_W7yeVeBkRLfIk,5058
10250
10250
  simo/fleet/__pycache__/managers.cpython-38.pyc,sha256=Vmm23zoQnS3-uS5_WJt2n3wtjhLiEhLWaYxXJCU6Gts,1339
10251
10251
  simo/fleet/__pycache__/models.cpython-38.pyc,sha256=WUahZgETWlem5rVXlJ_vINFRM7OZWp5xpWXGMoeBXsM,14131
10252
10252
  simo/fleet/__pycache__/routing.cpython-38.pyc,sha256=aPrCmxFKVyB8R8ZbJDwdPdFfvT7CvobovvZeq_mqRgY,314
10253
10253
  simo/fleet/__pycache__/serializers.cpython-38.pyc,sha256=gIWHJaSUbTe9H_xerD29Fz7BxIqXNzBI60GsIVXbNdY,3134
10254
- simo/fleet/__pycache__/socket_consumers.cpython-38.pyc,sha256=Dmy99vvfoB-k-lLvr_BHit6JzbCAXiUUxTSCpdDrElU,14012
10254
+ simo/fleet/__pycache__/socket_consumers.cpython-38.pyc,sha256=Hk0RlHcHDwVbKA_U1UVlZ3dWclR4N9fptUp3ZrgdGRs,14025
10255
10255
  simo/fleet/__pycache__/tasks.cpython-38.pyc,sha256=RoNxL2WUiW67s9O9DjaYVVjCBSZu2nje0Qn9FJkWVS0,1116
10256
10256
  simo/fleet/__pycache__/utils.cpython-38.pyc,sha256=obUd-X2Y-ybx4icqUWq_qwIxrP9yyarJjexWAfO4MTI,3344
10257
10257
  simo/fleet/__pycache__/views.cpython-38.pyc,sha256=wilxSvZliSKQ5qC7JjWneYBSdbeZeTsF5uDrOQVmvms,3181
@@ -10296,6 +10296,7 @@ simo/fleet/migrations/0038_alter_colonel_type.py,sha256=3NCAJc5M5BilQynd3lIvub3V
10296
10296
  simo/fleet/migrations/0039_auto_20241016_1047.py,sha256=c75iDlPH9NPQohkNBt58NPl31tNmctk5rww8wWQJmxA,801
10297
10297
  simo/fleet/migrations/0040_alter_colonel_pwm_frequency.py,sha256=AiiYeLXEmBAFj1O3WdJB0xQy1KqfiErYR8yPpL8W7e4,498
10298
10298
  simo/fleet/migrations/0041_alter_colonel_instance_and_more.py,sha256=r3PzZERt4kM0ul_TFl-i7qS7n0ht0G8jS83nucMxZfQ,1181
10299
+ simo/fleet/migrations/0042_auto_20241120_1028.py,sha256=nXyOXyTqhYfR2IDpIX0E3oIhKI7ShI7TGZ0Uf1CzR1U,715
10299
10300
  simo/fleet/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10300
10301
  simo/fleet/migrations/__pycache__/0001_initial.cpython-38.pyc,sha256=9kc1UyMEYkRNVnZ7iwZbiW1t3qWXROvWrI2G1BdzIaA,1250
10301
10302
  simo/fleet/migrations/__pycache__/0002_auto_20220422_0743.cpython-38.pyc,sha256=8oxhGb7rL8QYKlBLU3pOYcd8aHeQWDB9I8awkK04mXg,841
@@ -10338,6 +10339,7 @@ simo/fleet/migrations/__pycache__/0038_alter_colonel_type.cpython-38.pyc,sha256=
10338
10339
  simo/fleet/migrations/__pycache__/0039_auto_20241016_1047.cpython-38.pyc,sha256=6QU9LBUexU9GFSwPoBgLznO6xZU0N-HbWITah-W1xWo,1088
10339
10340
  simo/fleet/migrations/__pycache__/0040_alter_colonel_pwm_frequency.cpython-38.pyc,sha256=o_trHQUyQfYzWO_iWmZpvzc-uVJlBjK-GfyKhPOVSjc,736
10340
10341
  simo/fleet/migrations/__pycache__/0041_alter_colonel_instance_and_more.cpython-38.pyc,sha256=76yu9gz3pcAq5IYbNeZIoOSgdi0HVqTvc3EdXLGriQI,1178
10342
+ simo/fleet/migrations/__pycache__/0042_auto_20241120_1028.cpython-38.pyc,sha256=yK2HRc7MYMc4i9ev7FaguBdnXUnUmpFn8E8JOP_yluA,1030
10341
10343
  simo/fleet/migrations/__pycache__/__init__.cpython-38.pyc,sha256=5k1KW0jeSDzw6RnVPRq4CaO13Lg7M0F-pxA_gqqZ6Mg,170
10342
10344
  simo/fleet/templates/fleet/controllers_info/Button.md,sha256=GIuxqG617174NEtpPeCGVocxO4YMe7-CacgVSu_L5-E,739
10343
10345
  simo/fleet/templates/fleet/controllers_info/ENS160AirQualitySensor.md,sha256=3LSTY9YPFuVPIbVsYCAifcotrXJcOXl2k774_vo6nAE,770
@@ -10365,7 +10367,7 @@ simo/generic/migrations/__pycache__/0001_initial.cpython-38.pyc,sha256=xy_fN8vne
10365
10367
  simo/generic/migrations/__pycache__/__init__.cpython-38.pyc,sha256=nJV0NkIT8MuONj1hUX-V6aCU2lX3BXHyPjisapnBsPA,172
10366
10368
  simo/generic/scripting/__init__.py,sha256=aZZvNBae7unnux_zGHCIWCV2z47hVJc-DIL72Hqfkeo,600
10367
10369
  simo/generic/scripting/example.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10368
- simo/generic/scripting/helpers.py,sha256=Zt8Mx5AXIggzYk0e7jn-xQNR_NOqzolAReLkrmDJzVQ,3042
10370
+ simo/generic/scripting/helpers.py,sha256=JtGkx_ta5ZQSTsE7xgsyV3R-nM-jgcTKf3FHThLw82c,3887
10369
10371
  simo/generic/scripting/serializers.py,sha256=PjyFrjdPK1mBsgbNhyqMi9SWzcymqTa742ipy0LhAN4,1996
10370
10372
  simo/generic/scripting/__pycache__/__init__.cpython-38.pyc,sha256=eHncoNpv5dy35IO1_htWd8CK0sHFBnU_WJ0hl5TKOHQ,794
10371
10373
  simo/generic/scripting/__pycache__/helpers.cpython-38.pyc,sha256=34sa3L2cK1Aw636PCaoCYIWUBIE1h6XmbgDIto9cLeo,2757
@@ -10446,38 +10448,38 @@ simo/notifications/migrations/__pycache__/0002_notification_instance.cpython-38.
10446
10448
  simo/notifications/migrations/__pycache__/0003_alter_notification_instance.cpython-38.pyc,sha256=awhD1F9RyK_706zVNM5io3WT_konFkKQgL7D5MkONwk,851
10447
10449
  simo/notifications/migrations/__pycache__/__init__.cpython-38.pyc,sha256=YMBRHVon2nWDtIUbghckjnC12sIg_ykPWhV5aM0tto4,178
10448
10450
  simo/users/__init__.py,sha256=6a7uBpCWB_DR7p54rbHusc0xvi1qfT1ZCCQGb6TiBh8,52
10449
- simo/users/admin.py,sha256=SWKSb4giF69By3JYXuPMEd71Z_aCyin4_9NtIRJOY6I,7237
10450
- simo/users/api.py,sha256=03QFEOnIlACetS4QsPPBdagsC15_ap0-I64lCnplitI,12651
10451
+ simo/users/admin.py,sha256=nmFNHWckp73YZN5MV6ez6ommlj4MSI1Sr8BnQdSKW5g,7187
10452
+ simo/users/api.py,sha256=O3PYSk8BSX2hsi22cHSgqKQXViL9b3-xgBU2oJDfQGA,12443
10451
10453
  simo/users/apps.py,sha256=cq0A8-U1HALEwev0TicgFhr4CAu7Icz8rwq0HfOaL4E,207
10452
10454
  simo/users/auth_backends.py,sha256=KIw2AdjCUKfm_7Lql6aC4qdE6JznP0ECIMA5MVMLeiM,4251
10453
10455
  simo/users/auto_urls.py,sha256=lcJvteBsbHQMJieZpDz-63tDYejLApqsW3CUnDakd7k,272
10454
10456
  simo/users/dynamic_settings.py,sha256=sEIsi4yJw3kH46Jq_aOkSuK7QTfQACGUE-lkyBogCaM,570
10455
10457
  simo/users/managers.py,sha256=OHgEP85MBtdkdYxdstBd8RavTBT8F_2WyDxUJ9aCqqM,246
10456
10458
  simo/users/middleware.py,sha256=GMCrnWSc_2qCleyQIkfQGdL-pU-UTEcSg1wPvIKZ9uk,1210
10457
- simo/users/models.py,sha256=2NAWk3n_bg7JLHh5y8_dl2-7VZ97HTvhu61kNZYdaJg,19760
10459
+ simo/users/models.py,sha256=y27FK7nSUiAwzmOVvRCZz532yMRuuBQR2znRmorQ1d8,19686
10458
10460
  simo/users/permissions.py,sha256=IwtYS8yQdupWbYKR9VimSRDV3qCJ2jXP57Lyjpb2EQM,242
10459
10461
  simo/users/serializers.py,sha256=zzw1KONTnaTNBaU0r4rNVxJ827KzD6Z5LuQt27ZsQ98,2516
10460
10462
  simo/users/sso_urls.py,sha256=gQOaPvGMYFD0NCVSwyoWO-mTEHe5j9sbzV_RK7kdvp0,251
10461
10463
  simo/users/sso_views.py,sha256=5J0D4qUFQDvd-Fcqx_xLJWLJgPdqtVD5DDiPJyPsT2Q,4336
10462
- simo/users/tasks.py,sha256=3oYo7xv6XIbrl9qY3JuCYJMncRDDXAmfoV59oYIHyXo,1110
10463
- simo/users/utils.py,sha256=E9t6qljw3GARNMlH5qW7A_Ly1GaSNeAJcacxkQBJZyg,5457
10464
+ simo/users/tasks.py,sha256=M8MDQ1oR6WXWoOP5SQYui-CsCIV0jUmfI84TbRgGKo8,1199
10465
+ simo/users/utils.py,sha256=-Lu7C_sz8JJauLaIwgEa6umArqzNL7JJTY-ZcuF4irA,2004
10464
10466
  simo/users/views.py,sha256=dOQVvmlHG7ihWKJLFUBcqKOA0UDctlMKR0pTc36JZqg,3487
10465
10467
  simo/users/__pycache__/__init__.cpython-38.pyc,sha256=VFoDJE_SKKaPqqYaaBYd1Ndb1hjakkTo_u0EG_XJ1GM,211
10466
- simo/users/__pycache__/admin.cpython-38.pyc,sha256=1HKy4UfOLaokGkk3s-eJgMEpa4snLEL7qA-b0vT3Rrc,8237
10467
- simo/users/__pycache__/api.cpython-38.pyc,sha256=hO7qFF6oG-llh6Pm4y_3IU1KHgwWXBy_uqKdOKAK7Gc,10049
10468
+ simo/users/__pycache__/admin.cpython-38.pyc,sha256=Jcwn0jqueQkmgXPb327-3iUtrSycGzM537JB_-sryOM,8213
10469
+ simo/users/__pycache__/api.cpython-38.pyc,sha256=bwjxYhY4MBPbKLj-wJkTFZxHSqUE0fIC4Pw4lrHw9g4,9980
10468
10470
  simo/users/__pycache__/apps.cpython-38.pyc,sha256=dgbWL8CxzzISJQTmq_4IztPJ2UzykNVdqA2Ae1PmeGk,605
10469
10471
  simo/users/__pycache__/auth_backends.cpython-38.pyc,sha256=jYS2hlbTZh_ZtPeWcN50pc0IpyfCSO7_MvIbuVwEp8M,3144
10470
10472
  simo/users/__pycache__/auto_urls.cpython-38.pyc,sha256=K-3sz2h-cEitoflSmZk1t0eUg5mQMMGLNZFREVwG7_o,430
10471
10473
  simo/users/__pycache__/dynamic_settings.cpython-38.pyc,sha256=6F8JBjZkHykySnmZjNEzjS0ijbmPdcp9yUAZ5kqq_Fo,864
10472
10474
  simo/users/__pycache__/managers.cpython-38.pyc,sha256=O0Y8ABp42RAosrbODmYsPMaj9AyOPyJ-aqzuO0Qpi2s,679
10473
10475
  simo/users/__pycache__/middleware.cpython-38.pyc,sha256=Tj4nVEAvxEW3xA63fBRiJWRJpz_M848ZOqbHioc_IPE,1149
10474
- simo/users/__pycache__/models.cpython-38.pyc,sha256=fPZcTqgvuEokKa_TQyRA2eOZNwK_JYTvzTp1tyF3RwE,17716
10476
+ simo/users/__pycache__/models.cpython-38.pyc,sha256=0k1XtvS7y0Wga0FSb1bMSi6TUSzucDLsla_9dpYWg30,17681
10475
10477
  simo/users/__pycache__/permissions.cpython-38.pyc,sha256=ez5NxoL_JUeeH6GsKhvFreuA3FCBgGf9floSypdXUtM,633
10476
10478
  simo/users/__pycache__/serializers.cpython-38.pyc,sha256=Dy8RAcwNkNSXoJHvLp8fozURyHCtucqpSPyqZtbnMZc,3732
10477
10479
  simo/users/__pycache__/sso_urls.cpython-38.pyc,sha256=uAwDozpOmrhUald-8tOHANILXkH7-TI8fNYXOtPkSY8,402
10478
10480
  simo/users/__pycache__/sso_views.cpython-38.pyc,sha256=PLRF6FYCxRhnmgnN_gUS-pdQlH7lofLU1Xhgw3vDO_Y,4019
10479
10481
  simo/users/__pycache__/tasks.cpython-38.pyc,sha256=XLMKt3suT7BlcXrJZoH9ZIhhtBuqyiW4lsOB9IbBkko,1225
10480
- simo/users/__pycache__/utils.cpython-38.pyc,sha256=9qaNWqNX-5cFMJd9m6qtN-LyiLsVB97ea8y4E9Pr3DM,4701
10482
+ simo/users/__pycache__/utils.cpython-38.pyc,sha256=cgxUwEaBgxT360mw4E03J7u4vi3dhw6K3n7-8WNvyFU,1888
10481
10483
  simo/users/__pycache__/views.cpython-38.pyc,sha256=AXuUnVYRD0ai4FSFDp4qJwryukujAoN6LD3oIj-Cv3o,2426
10482
10484
  simo/users/migrations/0001_initial.py,sha256=_SnJemhNOs8Jjj-PjyvTVCBoxfs5V3lR_4ypUHUdLUg,7017
10483
10485
  simo/users/migrations/0002_componentpermission.py,sha256=rH9pC9HERf_5WWn3LCsNiu03BiHqURTF62pSNfswStI,918
@@ -10519,6 +10521,8 @@ simo/users/migrations/0037_rename_last_seen_location_datetime_instanceuser_last_
10519
10521
  simo/users/migrations/0038_userdevicereportlog_at_home_and_more.py,sha256=qL1ZjUJDiSrJat59ToqpNBwnMMPb3Q5mwHq-qd1eFcI,691
10520
10522
  simo/users/migrations/0039_auto_20241117_1039.py,sha256=e64AJM2ZId516Px-gmAxkp2NmSC5Vjo_BBTGbYrFuKY,1310
10521
10523
  simo/users/migrations/0040_userdevicereportlog_location_smoothed_and_more.py,sha256=umGDjQGpCrQocNxaCvwdTsOmSxcRC61NcUdHGUczoc4,855
10524
+ simo/users/migrations/0041_userdevicereportlog_speed_kmh_received.py,sha256=WoJ-ImX1lNIl7CKO_HMt3jbFjoenFGBUm9HZWidb-eA,433
10525
+ simo/users/migrations/0042_remove_userdevicereportlog_location_smoothed_and_more.py,sha256=p-GFu6qjkxqeA0TI8aaIum0k6fQfc39RIKbfGPSf0Yk,499
10522
10526
  simo/users/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10523
10527
  simo/users/migrations/__pycache__/0001_initial.cpython-38.pyc,sha256=e4XOKaYRb7l0P7cBnHHi5FQQJMlwjK0g7iqgM-xKmNI,4215
10524
10528
  simo/users/migrations/__pycache__/0002_componentpermission.cpython-38.pyc,sha256=pknJnpic8p6Vdx9DX41FfODXNnvexDswJtUCmC5w1tg,995
@@ -10562,6 +10566,8 @@ simo/users/migrations/__pycache__/0037_rename_last_seen_location_datetime_instan
10562
10566
  simo/users/migrations/__pycache__/0038_userdevicereportlog_at_home_and_more.cpython-38.pyc,sha256=khDSeTH3-jJ4yO1D6-i3Pm_NekJkVwBSUJ-rAxu0cr4,836
10563
10567
  simo/users/migrations/__pycache__/0039_auto_20241117_1039.cpython-38.pyc,sha256=IEOIfvnUiV-GX9VI4W2UKJugC3nfwKfy7QdmS3pW6Ss,1377
10564
10568
  simo/users/migrations/__pycache__/0040_userdevicereportlog_location_smoothed_and_more.cpython-38.pyc,sha256=I0W00mt73gjKunnwAyf3kz1FKfgm2HRwCJY_ogATSS0,918
10569
+ simo/users/migrations/__pycache__/0041_userdevicereportlog_speed_kmh_received.cpython-38.pyc,sha256=XWC9Emcc1Bm498UZ-6Lo7-i2Vq77vndjMA9xRdJbdfU,676
10570
+ simo/users/migrations/__pycache__/0042_remove_userdevicereportlog_location_smoothed_and_more.cpython-38.pyc,sha256=sSnWjU7rg5LXlRCxUFb_m2lUIcB4EHgtAnl-C9MMJvE,656
10565
10571
  simo/users/migrations/__pycache__/__init__.cpython-38.pyc,sha256=NKq7WLgktK8WV1oOqCPbAbdkrPV5GRGhYx4VxxI4dcs,170
10566
10572
  simo/users/templates/conf/mosquitto.conf,sha256=1eIGNuRu4Y3hfAU6qiWix648eCRrw0oOT24PnyFI4ys,189
10567
10573
  simo/users/templates/conf/mosquitto_acls.conf,sha256=ga44caTDNQE0CBKw55iM2jOuna6-9fKGwAhjyERZdRE,500
@@ -10571,9 +10577,9 @@ simo/users/templates/invitations/expired_msg.html,sha256=47DEQpj8HBSa-_TImW-5JCe
10571
10577
  simo/users/templates/invitations/expired_suggestion.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10572
10578
  simo/users/templates/invitations/taken_msg.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10573
10579
  simo/users/templates/invitations/taken_suggestion.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10574
- simo-2.5.31.dist-info/LICENSE.md,sha256=M7wm1EmMGDtwPRdg7kW4d00h1uAXjKOT3HFScYQMeiE,34916
10575
- simo-2.5.31.dist-info/METADATA,sha256=nFDDYX4oUK_5gtNMScQSzfboiiS7didaCErf8noKlDM,1953
10576
- simo-2.5.31.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
10577
- simo-2.5.31.dist-info/entry_points.txt,sha256=S9PwnUYmTSW7681GKDCxUbL0leRJIaRk6fDQIKgbZBA,135
10578
- simo-2.5.31.dist-info/top_level.txt,sha256=GmS1hrAbpVqn9OWZh6UX82eIOdRLgYA82RG9fe8v4Rs,5
10579
- simo-2.5.31.dist-info/RECORD,,
10580
+ simo-2.5.34.dist-info/LICENSE.md,sha256=M7wm1EmMGDtwPRdg7kW4d00h1uAXjKOT3HFScYQMeiE,34916
10581
+ simo-2.5.34.dist-info/METADATA,sha256=It5zSxA-pwB_V8rKlMy-sHyM3fLZgFds8V4Seg4XChc,1953
10582
+ simo-2.5.34.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
10583
+ simo-2.5.34.dist-info/entry_points.txt,sha256=S9PwnUYmTSW7681GKDCxUbL0leRJIaRk6fDQIKgbZBA,135
10584
+ simo-2.5.34.dist-info/top_level.txt,sha256=GmS1hrAbpVqn9OWZh6UX82eIOdRLgYA82RG9fe8v4Rs,5
10585
+ simo-2.5.34.dist-info/RECORD,,
File without changes