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.
- simo/core/__pycache__/autocomplete_views.cpython-38.pyc +0 -0
- simo/core/__pycache__/controllers.cpython-38.pyc +0 -0
- simo/core/__pycache__/tasks.cpython-38.pyc +0 -0
- simo/core/autocomplete_views.py +1 -1
- simo/core/controllers.py +1 -2
- simo/core/forms.py +2 -1
- simo/core/tasks.py +16 -1
- simo/core/utils/__pycache__/helpers.cpython-38.pyc +0 -0
- simo/fleet/__pycache__/forms.cpython-38.pyc +0 -0
- simo/fleet/__pycache__/socket_consumers.cpython-38.pyc +0 -0
- simo/fleet/forms.py +51 -19
- simo/fleet/migrations/0042_auto_20241120_1028.py +30 -0
- simo/fleet/migrations/__pycache__/0042_auto_20241120_1028.cpython-38.pyc +0 -0
- simo/fleet/socket_consumers.py +1 -0
- simo/fleet/tasks.py +2 -0
- simo/generic/scripting/helpers.py +23 -1
- simo/users/__pycache__/admin.cpython-38.pyc +0 -0
- simo/users/__pycache__/api.cpython-38.pyc +0 -0
- simo/users/__pycache__/models.cpython-38.pyc +0 -0
- simo/users/__pycache__/utils.cpython-38.pyc +0 -0
- simo/users/admin.py +2 -3
- simo/users/api.py +15 -16
- simo/users/migrations/0041_userdevicereportlog_speed_kmh_received.py +18 -0
- simo/users/migrations/0042_remove_userdevicereportlog_location_smoothed_and_more.py +21 -0
- simo/users/migrations/__pycache__/0041_userdevicereportlog_speed_kmh_received.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0042_remove_userdevicereportlog_location_smoothed_and_more.cpython-38.pyc +0 -0
- simo/users/models.py +0 -1
- simo/users/tasks.py +2 -0
- simo/users/utils.py +3 -100
- {simo-2.5.31.dist-info → simo-2.5.34.dist-info}/METADATA +1 -1
- {simo-2.5.31.dist-info → simo-2.5.34.dist-info}/RECORD +35 -29
- {simo-2.5.31.dist-info → simo-2.5.34.dist-info}/LICENSE.md +0 -0
- {simo-2.5.31.dist-info → simo-2.5.34.dist-info}/WHEEL +0 -0
- {simo-2.5.31.dist-info → simo-2.5.34.dist-info}/entry_points.txt +0 -0
- {simo-2.5.31.dist-info → simo-2.5.34.dist-info}/top_level.txt +0 -0
|
Binary file
|
|
Binary file
|
|
Binary file
|
simo/core/autocomplete_views.py
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
Binary file
|
|
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.
|
|
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
|
-
|
|
1268
|
-
label="
|
|
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")),
|
|
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
|
|
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
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
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('
|
|
1323
|
-
self._clean_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
|
|
1354
|
-
self.instance.config['
|
|
1355
|
-
self.cleaned_data['
|
|
1356
|
-
if
|
|
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
|
+
]
|
|
Binary file
|
simo/fleet/socket_consumers.py
CHANGED
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
|
|
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', '
|
|
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', '
|
|
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
|
-
|
|
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
|
-
|
|
223
|
+
|
|
224
224
|
|
|
225
225
|
at_home = False
|
|
226
226
|
if not relay:
|
|
227
227
|
at_home = True
|
|
228
|
-
elif
|
|
228
|
+
elif location:
|
|
229
229
|
at_home = haversine_distance(
|
|
230
|
-
self.instance.location,
|
|
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
|
|
236
|
+
elif location:
|
|
237
237
|
iu.at_home = haversine_distance(
|
|
238
|
-
iu.instance.location,
|
|
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 =
|
|
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,
|
|
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
|
+
]
|
|
Binary file
|
|
Binary file
|
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
|
+
)
|
|
@@ -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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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
|
|
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=
|
|
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=
|
|
10238
|
-
simo/fleet/tasks.py,sha256=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
10450
|
-
simo/users/api.py,sha256=
|
|
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=
|
|
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=
|
|
10463
|
-
simo/users/utils.py,sha256
|
|
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=
|
|
10467
|
-
simo/users/__pycache__/api.cpython-38.pyc,sha256=
|
|
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=
|
|
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=
|
|
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.
|
|
10575
|
-
simo-2.5.
|
|
10576
|
-
simo-2.5.
|
|
10577
|
-
simo-2.5.
|
|
10578
|
-
simo-2.5.
|
|
10579
|
-
simo-2.5.
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|