simo 2.0.25__py3-none-any.whl → 2.0.26__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__/api.cpython-38.pyc +0 -0
- simo/core/__pycache__/forms.cpython-38.pyc +0 -0
- simo/core/__pycache__/permissions.cpython-38.pyc +0 -0
- simo/core/__pycache__/serializers.cpython-38.pyc +0 -0
- simo/core/api.py +15 -3
- simo/core/forms.py +29 -1
- simo/core/migrations/0032_auto_20240506_0834.py +24 -0
- simo/core/migrations/__pycache__/0032_auto_20240506_0834.cpython-38.pyc +0 -0
- simo/core/permissions.py +3 -0
- simo/core/serializers.py +20 -3
- simo/fleet/__pycache__/forms.cpython-38.pyc +0 -0
- simo/fleet/forms.py +121 -172
- simo/generic/__pycache__/forms.cpython-38.pyc +0 -0
- simo/generic/forms.py +24 -25
- simo/users/__pycache__/models.cpython-38.pyc +0 -0
- simo/users/migrations/0027_permissionsrole_can_manage_components.py +18 -0
- simo/users/migrations/0028_auto_20240506_1146.py +22 -0
- simo/users/migrations/__pycache__/0027_permissionsrole_can_manage_components.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0028_auto_20240506_1146.cpython-38.pyc +0 -0
- simo/users/models.py +7 -6
- {simo-2.0.25.dist-info → simo-2.0.26.dist-info}/METADATA +1 -1
- {simo-2.0.25.dist-info → simo-2.0.26.dist-info}/RECORD +25 -19
- {simo-2.0.25.dist-info → simo-2.0.26.dist-info}/LICENSE.md +0 -0
- {simo-2.0.25.dist-info → simo-2.0.26.dist-info}/WHEEL +0 -0
- {simo-2.0.25.dist-info → simo-2.0.26.dist-info}/top_level.txt +0 -0
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
simo/core/api.py
CHANGED
|
@@ -217,7 +217,10 @@ class ComponentViewSet(
|
|
|
217
217
|
@action(detail=True, methods=['post'])
|
|
218
218
|
def subcomponent(self, request, pk=None, *args, **kwargs):
|
|
219
219
|
component = self.get_object()
|
|
220
|
-
|
|
220
|
+
data = request.data
|
|
221
|
+
if not isinstance(request.data, dict):
|
|
222
|
+
data = data.dict()
|
|
223
|
+
json_data = restore_json(data)
|
|
221
224
|
subcomponent_id = json_data.pop('id', -1)
|
|
222
225
|
try:
|
|
223
226
|
subcomponent = component.slaves.get(pk=subcomponent_id)
|
|
@@ -237,13 +240,20 @@ class ComponentViewSet(
|
|
|
237
240
|
@action(detail=True, methods=['post'])
|
|
238
241
|
def controller(self, request, pk=None, *args, **kwargs):
|
|
239
242
|
component = self.get_object()
|
|
243
|
+
data = request.data
|
|
244
|
+
if not isinstance(request.data, dict):
|
|
245
|
+
data = data.dict()
|
|
246
|
+
request_data = restore_json(data)
|
|
240
247
|
return self.perform_controller_method(
|
|
241
|
-
restore_json(
|
|
248
|
+
restore_json(request_data), component
|
|
242
249
|
)
|
|
243
250
|
|
|
244
251
|
@action(detail=False, methods=['post'])
|
|
245
252
|
def control(self, request, *args, **kwargs):
|
|
246
|
-
|
|
253
|
+
data = request.data
|
|
254
|
+
if not isinstance(request.data, dict):
|
|
255
|
+
data = data.dict()
|
|
256
|
+
request_data = restore_json(data)
|
|
247
257
|
component = self.get_queryset().filter(id=request_data.pop('id', 0)).first()
|
|
248
258
|
if not component:
|
|
249
259
|
raise Http404()
|
|
@@ -262,6 +272,8 @@ class ComponentViewSet(
|
|
|
262
272
|
return RESTResponse(resp_data)
|
|
263
273
|
|
|
264
274
|
|
|
275
|
+
|
|
276
|
+
|
|
265
277
|
class HistoryResultsSetPagination(PageNumberPagination):
|
|
266
278
|
page_size = 50
|
|
267
279
|
page_size_query_param = 'page_size'
|
simo/core/forms.py
CHANGED
|
@@ -195,6 +195,9 @@ class ConfigFieldsMixin:
|
|
|
195
195
|
|
|
196
196
|
def save(self, commit=True):
|
|
197
197
|
for field_name in self.config_fields:
|
|
198
|
+
# support for partial forms
|
|
199
|
+
if field_name not in self.cleaned_data:
|
|
200
|
+
continue
|
|
198
201
|
if isinstance(self.cleaned_data[field_name], models.Model):
|
|
199
202
|
self.instance.config[field_name] = \
|
|
200
203
|
self.cleaned_data[field_name].pk
|
|
@@ -282,6 +285,10 @@ class ComponentAdminForm(forms.ModelForm):
|
|
|
282
285
|
has_icon = True
|
|
283
286
|
has_alarm = True
|
|
284
287
|
|
|
288
|
+
# fields that can be edited via SIMO.io app by instance owners.
|
|
289
|
+
# Users who have is_owner enabled on their user role.
|
|
290
|
+
basic_fields = ['name', 'icon', 'zone', 'category']
|
|
291
|
+
|
|
285
292
|
class Meta:
|
|
286
293
|
model = Component
|
|
287
294
|
fields = '__all__'
|
|
@@ -324,6 +331,27 @@ class ComponentAdminForm(forms.ModelForm):
|
|
|
324
331
|
self.instance.config = self.controller.default_config
|
|
325
332
|
self.instance.meta = self.controller.default_meta
|
|
326
333
|
|
|
334
|
+
self.cleanup_missing_keys(kwargs.get("data"))
|
|
335
|
+
|
|
336
|
+
def cleanup_missing_keys(self, data):
|
|
337
|
+
"""
|
|
338
|
+
Removes missing keys from fields on form submission.
|
|
339
|
+
This avoids resetting fields that are not present in
|
|
340
|
+
the submitted data, which may be the sign of a buggy
|
|
341
|
+
or incomplete template.
|
|
342
|
+
Note that this cleanup relies on the HTML form being
|
|
343
|
+
patched to send all keys, even for checkboxes, via
|
|
344
|
+
input[type="hidden"] fields or some JS magic.
|
|
345
|
+
"""
|
|
346
|
+
if data is None:
|
|
347
|
+
# not a form submission, don't modify self.fields
|
|
348
|
+
return
|
|
349
|
+
|
|
350
|
+
got_keys = data.keys()
|
|
351
|
+
field_names = self.fields.keys()
|
|
352
|
+
for missing in set(field_names) - set(got_keys):
|
|
353
|
+
del self.fields[missing]
|
|
354
|
+
|
|
327
355
|
@classmethod
|
|
328
356
|
def get_admin_fieldsets(cls, request, obj=None):
|
|
329
357
|
main_fields = (
|
|
@@ -518,7 +546,7 @@ class SwitchForm(BaseComponentForm):
|
|
|
518
546
|
|
|
519
547
|
def save(self, commit=True):
|
|
520
548
|
obj = super().save(commit=commit)
|
|
521
|
-
if commit:
|
|
549
|
+
if commit and 'slaves' in self.cleaned_data:
|
|
522
550
|
obj.slaves.set(self.cleaned_data['slaves'])
|
|
523
551
|
return obj
|
|
524
552
|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Generated by Django 3.2.9 on 2024-05-06 08:34
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
import django.db.models.deletion
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Migration(migrations.Migration):
|
|
8
|
+
|
|
9
|
+
dependencies = [
|
|
10
|
+
('core', '0031_auto_20240429_1231'),
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
operations = [
|
|
14
|
+
migrations.AlterField(
|
|
15
|
+
model_name='category',
|
|
16
|
+
name='order',
|
|
17
|
+
field=models.PositiveIntegerField(db_index=True),
|
|
18
|
+
),
|
|
19
|
+
migrations.AlterField(
|
|
20
|
+
model_name='instance',
|
|
21
|
+
name='indoor_climate_sensor',
|
|
22
|
+
field=models.ForeignKey(blank=True, limit_choices_to={'base_type__in': ['numeric-sensor', 'multi-sensor']}, null=True, on_delete=django.db.models.deletion.SET_NULL, to='core.component'),
|
|
23
|
+
),
|
|
24
|
+
]
|
|
Binary file
|
simo/core/permissions.py
CHANGED
simo/core/serializers.py
CHANGED
|
@@ -261,6 +261,12 @@ class ComponentSerializer(FormSerializer):
|
|
|
261
261
|
self.set_form_cls()
|
|
262
262
|
|
|
263
263
|
ret = OrderedDict()
|
|
264
|
+
if not self.context['request'].user.is_master:
|
|
265
|
+
user_role = self.context['request'].user.get_role(
|
|
266
|
+
self.context['instance']
|
|
267
|
+
)
|
|
268
|
+
if not any([user_role.is_superuser, user_role.is_owner]):
|
|
269
|
+
return ret
|
|
264
270
|
|
|
265
271
|
field_mapping = reduce_attr_dict_from_instance(
|
|
266
272
|
self,
|
|
@@ -269,9 +275,10 @@ class ComponentSerializer(FormSerializer):
|
|
|
269
275
|
)
|
|
270
276
|
|
|
271
277
|
if not self.instance or isinstance(self.instance, Iterable):
|
|
272
|
-
form = self.
|
|
278
|
+
form = self.get_form()
|
|
273
279
|
else:
|
|
274
|
-
form = self.
|
|
280
|
+
form = self.get_form(instance=self.instance)
|
|
281
|
+
|
|
275
282
|
for field_name in form.fields:
|
|
276
283
|
# if field is specified as excluded field
|
|
277
284
|
if field_name in getattr(self.Meta, 'exclude', []):
|
|
@@ -355,7 +362,7 @@ class ComponentSerializer(FormSerializer):
|
|
|
355
362
|
|
|
356
363
|
def get_form(self, data=None, **kwargs):
|
|
357
364
|
self.set_form_cls()
|
|
358
|
-
if not self.instance:
|
|
365
|
+
if not self.instance or isinstance(self.instance, Iterable):
|
|
359
366
|
#controller_uid = 'simo.generic.controllers.AlarmClock'
|
|
360
367
|
controller_uid = self.context['request'].META.get('HTTP_CONTROLLER')
|
|
361
368
|
else:
|
|
@@ -365,6 +372,14 @@ class ComponentSerializer(FormSerializer):
|
|
|
365
372
|
controller_uid=controller_uid,
|
|
366
373
|
**kwargs
|
|
367
374
|
)
|
|
375
|
+
if not self.context['request'].user.is_master:
|
|
376
|
+
user_role = self.context['request'].user.get_role(
|
|
377
|
+
self.context['instance']
|
|
378
|
+
)
|
|
379
|
+
if not user_role.is_superuser and user_role.is_owner:
|
|
380
|
+
for field_name in list(form.fields.keys()):
|
|
381
|
+
if field_name not in form.basic_fields:
|
|
382
|
+
del form.fields[field_name]
|
|
368
383
|
return form
|
|
369
384
|
|
|
370
385
|
def accomodate_formsets(self, form, data):
|
|
@@ -395,6 +410,7 @@ class ComponentSerializer(FormSerializer):
|
|
|
395
410
|
)
|
|
396
411
|
form = self.get_form(instance=self.instance)
|
|
397
412
|
a_data = self.accomodate_formsets(form, data)
|
|
413
|
+
|
|
398
414
|
form = self.get_form(
|
|
399
415
|
data=a_data, instance=self.instance
|
|
400
416
|
)
|
|
@@ -410,6 +426,7 @@ class ComponentSerializer(FormSerializer):
|
|
|
410
426
|
a_data = self.accomodate_formsets(form, validated_data)
|
|
411
427
|
form = self.get_form(instance=instance, data=a_data)
|
|
412
428
|
if form.is_valid():
|
|
429
|
+
print("FORM FIELDS", form.fields)
|
|
413
430
|
instance = form.save(commit=True)
|
|
414
431
|
return instance
|
|
415
432
|
raise serializers.ValidationError(form.errors)
|
|
Binary file
|
simo/fleet/forms.py
CHANGED
|
@@ -91,13 +91,16 @@ class ColonelComponentForm(BaseComponentForm):
|
|
|
91
91
|
def clean_colonel(self):
|
|
92
92
|
if not self.instance.pk:
|
|
93
93
|
return self.cleaned_data['colonel']
|
|
94
|
+
colonel = self.cleaned_data.get('colonel')
|
|
95
|
+
if not colonel:
|
|
96
|
+
return
|
|
94
97
|
org = self.instance.config.get('colonel')
|
|
95
|
-
if org and org !=
|
|
98
|
+
if org and org != colonel.id:
|
|
96
99
|
raise forms.ValidationError(
|
|
97
100
|
"Changing colonel after component is created "
|
|
98
101
|
"it is not allowed!"
|
|
99
102
|
)
|
|
100
|
-
return
|
|
103
|
+
return colonel
|
|
101
104
|
|
|
102
105
|
def _clean_pin(self, field_name):
|
|
103
106
|
if self.cleaned_data[field_name].colonel != self.cleaned_data['colonel']:
|
|
@@ -153,7 +156,7 @@ class ColonelComponentForm(BaseComponentForm):
|
|
|
153
156
|
|
|
154
157
|
def save(self, commit=True):
|
|
155
158
|
obj = super().save(commit)
|
|
156
|
-
if commit:
|
|
159
|
+
if commit and 'colonel' in self.cleaned_data:
|
|
157
160
|
self.cleaned_data['colonel'].components.add(obj)
|
|
158
161
|
self.cleaned_data['colonel'].rebuild_occupied_pins()
|
|
159
162
|
self.cleaned_data['colonel'].save()
|
|
@@ -211,7 +214,7 @@ class ColonelBinarySensorConfigForm(ColonelComponentForm):
|
|
|
211
214
|
|
|
212
215
|
def clean(self):
|
|
213
216
|
super().clean()
|
|
214
|
-
if not self.cleaned_data
|
|
217
|
+
if 'colonel' not in self.cleaned_data:
|
|
215
218
|
return self.cleaned_data
|
|
216
219
|
if 'pin' not in self.cleaned_data:
|
|
217
220
|
return self.cleaned_data
|
|
@@ -251,7 +254,8 @@ class ColonelBinarySensorConfigForm(ColonelComponentForm):
|
|
|
251
254
|
|
|
252
255
|
|
|
253
256
|
def save(self, commit=True):
|
|
254
|
-
|
|
257
|
+
if 'pin' in self.cleaned_data:
|
|
258
|
+
self.instance.config['pin_no'] = self.cleaned_data['pin'].no
|
|
255
259
|
return super().save(commit=commit)
|
|
256
260
|
|
|
257
261
|
|
|
@@ -291,7 +295,7 @@ class ColonelNumericSensorConfigForm(ColonelComponentForm, NumericSensorForm):
|
|
|
291
295
|
|
|
292
296
|
def clean(self):
|
|
293
297
|
super().clean()
|
|
294
|
-
if not self.cleaned_data
|
|
298
|
+
if 'colonel' not in self.cleaned_data:
|
|
295
299
|
return self.cleaned_data
|
|
296
300
|
if 'pin' not in self.cleaned_data:
|
|
297
301
|
return self.cleaned_data
|
|
@@ -302,7 +306,8 @@ class ColonelNumericSensorConfigForm(ColonelComponentForm, NumericSensorForm):
|
|
|
302
306
|
|
|
303
307
|
|
|
304
308
|
def save(self, commit=True):
|
|
305
|
-
|
|
309
|
+
if 'pin' in self.cleaned_data:
|
|
310
|
+
self.instance.config['pin_no'] = self.cleaned_data['pin'].no
|
|
306
311
|
return super().save(commit=commit)
|
|
307
312
|
|
|
308
313
|
|
|
@@ -329,7 +334,7 @@ class DS18B20SensorConfigForm(ColonelComponentForm, NumericSensorForm):
|
|
|
329
334
|
|
|
330
335
|
def clean(self):
|
|
331
336
|
super().clean()
|
|
332
|
-
if not self.cleaned_data
|
|
337
|
+
if 'colonel' not in self.cleaned_data:
|
|
333
338
|
return self.cleaned_data
|
|
334
339
|
if 'pin' not in self.cleaned_data:
|
|
335
340
|
return self.cleaned_data
|
|
@@ -339,7 +344,8 @@ class DS18B20SensorConfigForm(ColonelComponentForm, NumericSensorForm):
|
|
|
339
344
|
return self.cleaned_data
|
|
340
345
|
|
|
341
346
|
def save(self, commit=True):
|
|
342
|
-
|
|
347
|
+
if 'pin' in self.cleaned_data:
|
|
348
|
+
self.instance.config['pin_no'] = self.cleaned_data['pin'].no
|
|
343
349
|
return super().save(commit=commit)
|
|
344
350
|
|
|
345
351
|
|
|
@@ -385,7 +391,8 @@ class ColonelDHTSensorConfigForm(ColonelComponentForm):
|
|
|
385
391
|
return self.cleaned_data
|
|
386
392
|
|
|
387
393
|
def save(self, commit=True):
|
|
388
|
-
|
|
394
|
+
if 'pin' in self.cleaned_data:
|
|
395
|
+
self.instance.config['pin_no'] = self.cleaned_data['pin'].no
|
|
389
396
|
return super().save(commit=commit)
|
|
390
397
|
|
|
391
398
|
|
|
@@ -415,7 +422,8 @@ class BME680SensorConfigForm(ColonelComponentForm):
|
|
|
415
422
|
)
|
|
416
423
|
|
|
417
424
|
def save(self, commit=True):
|
|
418
|
-
|
|
425
|
+
if 'interface' in self.cleaned_data:
|
|
426
|
+
self.instance.config['i2c_interface'] = self.cleaned_data['interface'].no
|
|
419
427
|
return super().save(commit=commit)
|
|
420
428
|
|
|
421
429
|
|
|
@@ -445,7 +453,8 @@ class MPC9808SensorConfigForm(ColonelComponentForm):
|
|
|
445
453
|
)
|
|
446
454
|
|
|
447
455
|
def save(self, commit=True):
|
|
448
|
-
|
|
456
|
+
if 'interface' in self.cleaned_data:
|
|
457
|
+
self.instance.config['i2c_interface'] = self.cleaned_data['interface'].no
|
|
449
458
|
return super().save(commit=commit)
|
|
450
459
|
|
|
451
460
|
|
|
@@ -472,7 +481,7 @@ class ColonelTouchSensorConfigForm(ColonelComponentForm):
|
|
|
472
481
|
|
|
473
482
|
def clean(self):
|
|
474
483
|
super().clean()
|
|
475
|
-
if not self.cleaned_data
|
|
484
|
+
if 'colonel' not in self.cleaned_data:
|
|
476
485
|
return self.cleaned_data
|
|
477
486
|
if 'pin' not in self.cleaned_data:
|
|
478
487
|
return self.cleaned_data
|
|
@@ -483,7 +492,8 @@ class ColonelTouchSensorConfigForm(ColonelComponentForm):
|
|
|
483
492
|
|
|
484
493
|
|
|
485
494
|
def save(self, commit=True):
|
|
486
|
-
|
|
495
|
+
if 'pin' in self.cleaned_data:
|
|
496
|
+
self.instance.config['pin_no'] = self.cleaned_data['pin'].no
|
|
487
497
|
return super().save(commit=commit)
|
|
488
498
|
|
|
489
499
|
|
|
@@ -528,35 +538,33 @@ class ColonelSwitchConfigForm(ColonelComponentForm):
|
|
|
528
538
|
|
|
529
539
|
def __init__(self, *args, **kwargs):
|
|
530
540
|
super().__init__(*args, **kwargs)
|
|
531
|
-
|
|
541
|
+
self.basic_fields.append('auto_off')
|
|
542
|
+
if self.instance.pk and 'slaves' in self.fields:
|
|
532
543
|
self.fields['slaves'].initial = self.instance.slaves.all()
|
|
533
544
|
|
|
534
545
|
def clean_slaves(self):
|
|
546
|
+
if 'slaves' not in self.cleaned_data:
|
|
547
|
+
return
|
|
535
548
|
if not self.cleaned_data['slaves'] or not self.instance:
|
|
536
549
|
return self.cleaned_data['slaves']
|
|
537
550
|
return validate_slaves(self.cleaned_data['slaves'], self.instance)
|
|
538
551
|
|
|
539
552
|
def clean(self):
|
|
540
553
|
super().clean()
|
|
541
|
-
if not self.cleaned_data.get('colonel'):
|
|
542
|
-
return self.cleaned_data
|
|
543
|
-
if not self.cleaned_data.get('output_pin'):
|
|
544
|
-
return self.cleaned_data
|
|
545
|
-
|
|
546
|
-
self._clean_pin('output_pin')
|
|
547
|
-
|
|
548
|
-
if not self.cleaned_data.get('controls'):
|
|
549
|
-
return self.cleaned_data
|
|
550
554
|
|
|
551
|
-
self.
|
|
555
|
+
if self.cleaned_data.get('output_pin'):
|
|
556
|
+
self._clean_pin('output_pin')
|
|
557
|
+
if self.cleaned_data.get('controls'):
|
|
558
|
+
self._clean_controls()
|
|
552
559
|
|
|
553
560
|
return self.cleaned_data
|
|
554
561
|
|
|
555
562
|
|
|
556
563
|
def save(self, commit=True):
|
|
557
|
-
|
|
564
|
+
if 'output_pin' in self.cleaned_data:
|
|
565
|
+
self.instance.config['output_pin_no'] = self.cleaned_data['output_pin'].no
|
|
558
566
|
obj = super().save(commit=commit)
|
|
559
|
-
if commit:
|
|
567
|
+
if commit and 'slaves' in self.cleaned_data:
|
|
560
568
|
obj.slaves.set(self.cleaned_data['slaves'])
|
|
561
569
|
return obj
|
|
562
570
|
|
|
@@ -628,7 +636,8 @@ class ColonelPWMOutputConfigForm(ColonelComponentForm):
|
|
|
628
636
|
|
|
629
637
|
def __init__(self, *args, **kwargs):
|
|
630
638
|
super().__init__(*args, **kwargs)
|
|
631
|
-
|
|
639
|
+
self.basic_fields.extend(['turn_on_time', 'turn_off_time', 'skew'])
|
|
640
|
+
if self.instance.pk and 'slaves' in self.fields:
|
|
632
641
|
self.fields['slaves'].initial = self.instance.slaves.all()
|
|
633
642
|
|
|
634
643
|
def clean_slaves(self):
|
|
@@ -638,25 +647,18 @@ class ColonelPWMOutputConfigForm(ColonelComponentForm):
|
|
|
638
647
|
|
|
639
648
|
def clean(self):
|
|
640
649
|
super().clean()
|
|
641
|
-
if
|
|
642
|
-
|
|
643
|
-
if
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
self._clean_pin('output_pin')
|
|
647
|
-
|
|
648
|
-
if not self.cleaned_data.get('controls'):
|
|
649
|
-
return self.cleaned_data
|
|
650
|
-
|
|
651
|
-
self._clean_controls()
|
|
652
|
-
|
|
650
|
+
if self.cleaned_data.get('output_pin'):
|
|
651
|
+
self._clean_pin('output_pin')
|
|
652
|
+
if self.cleaned_data.get('controls'):
|
|
653
|
+
self._clean_controls()
|
|
653
654
|
return self.cleaned_data
|
|
654
655
|
|
|
655
656
|
|
|
656
657
|
def save(self, commit=True):
|
|
657
|
-
|
|
658
|
+
if 'output_pin' in self.cleaned_data:
|
|
659
|
+
self.instance.config['output_pin_no'] = self.cleaned_data['output_pin'].no
|
|
658
660
|
obj = super().save(commit=commit)
|
|
659
|
-
if commit:
|
|
661
|
+
if commit and 'slaves' in self.cleaned_data:
|
|
660
662
|
obj.slaves.set(self.cleaned_data['slaves'])
|
|
661
663
|
return obj
|
|
662
664
|
|
|
@@ -716,7 +718,9 @@ class ColonelRGBLightConfigForm(ColonelComponentForm):
|
|
|
716
718
|
)
|
|
717
719
|
|
|
718
720
|
def save(self, commit=True):
|
|
719
|
-
|
|
721
|
+
if 'output_pin' in self.cleaned_data:
|
|
722
|
+
self.instance.config['output_pin_no'] = \
|
|
723
|
+
self.cleaned_data['output_pin'].no
|
|
720
724
|
return super().save(commit)
|
|
721
725
|
|
|
722
726
|
def clean_custom_timing(self):
|
|
@@ -740,31 +744,25 @@ class ColonelRGBLightConfigForm(ColonelComponentForm):
|
|
|
740
744
|
|
|
741
745
|
def clean(self):
|
|
742
746
|
super().clean()
|
|
743
|
-
if not self.cleaned_data.get('colonel'):
|
|
744
|
-
return self.cleaned_data
|
|
745
|
-
if not self.cleaned_data.get('output_pin'):
|
|
746
|
-
return self.cleaned_data
|
|
747
|
-
|
|
748
|
-
if self.cleaned_data.get('color_order'):
|
|
749
|
-
if self.cleaned_data['has_white']:
|
|
750
|
-
if len(self.cleaned_data['color_order']) != 4:
|
|
751
|
-
self.add_error(
|
|
752
|
-
"color_order",
|
|
753
|
-
_("4 colors expected for stripes with dedicated White led.")
|
|
754
|
-
)
|
|
755
|
-
else:
|
|
756
|
-
if len(self.cleaned_data['color_order']) != 3:
|
|
757
|
-
self.add_error(
|
|
758
|
-
"color_order",
|
|
759
|
-
_("3 colors expected for stripes without dedicated White led.")
|
|
760
|
-
)
|
|
761
747
|
|
|
762
748
|
self._clean_pin('output_pin')
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
749
|
+
if self.cleaned_data.get('controls'):
|
|
750
|
+
self._clean_controls()
|
|
751
|
+
|
|
752
|
+
if 'color_order' in self.cleaned_data:
|
|
753
|
+
if self.cleaned_data.get('color_order'):
|
|
754
|
+
if self.cleaned_data['has_white']:
|
|
755
|
+
if len(self.cleaned_data['color_order']) != 4:
|
|
756
|
+
self.add_error(
|
|
757
|
+
"color_order",
|
|
758
|
+
_("4 colors expected for stripes with dedicated White led.")
|
|
759
|
+
)
|
|
760
|
+
else:
|
|
761
|
+
if len(self.cleaned_data['color_order']) != 3:
|
|
762
|
+
self.add_error(
|
|
763
|
+
"color_order",
|
|
764
|
+
_("3 colors expected for stripes without dedicated White led.")
|
|
765
|
+
)
|
|
768
766
|
|
|
769
767
|
return self.cleaned_data
|
|
770
768
|
|
|
@@ -811,21 +809,19 @@ class DualMotorValveForm(ColonelComponentForm):
|
|
|
811
809
|
|
|
812
810
|
def clean(self):
|
|
813
811
|
super().clean()
|
|
814
|
-
if
|
|
815
|
-
|
|
816
|
-
if
|
|
817
|
-
|
|
818
|
-
if not self.cleaned_data.get('close_pin'):
|
|
819
|
-
return self.cleaned_data
|
|
820
|
-
|
|
821
|
-
self._clean_pin('open_pin')
|
|
822
|
-
self._clean_pin('close_pin')
|
|
823
|
-
|
|
812
|
+
if self.cleaned_data.get('open_pin'):
|
|
813
|
+
self._clean_pin('open_pin')
|
|
814
|
+
if self.cleaned_data.get('close_pin'):
|
|
815
|
+
self._clean_pin('close_pin')
|
|
824
816
|
return self.cleaned_data
|
|
825
817
|
|
|
826
818
|
def save(self, commit=True):
|
|
827
|
-
|
|
828
|
-
|
|
819
|
+
if 'open_pin' in self.cleaned_data:
|
|
820
|
+
self.instance.config['open_pin_no'] = \
|
|
821
|
+
self.cleaned_data['open_pin'].no
|
|
822
|
+
if 'close_pin' in self.cleaned_data:
|
|
823
|
+
self.instance.config['close_pin_no'] = \
|
|
824
|
+
self.cleaned_data['close_pin'].no
|
|
829
825
|
return super().save(commit=commit)
|
|
830
826
|
|
|
831
827
|
|
|
@@ -903,40 +899,37 @@ class BlindsConfigForm(ColonelComponentForm):
|
|
|
903
899
|
|
|
904
900
|
def clean(self):
|
|
905
901
|
super().clean()
|
|
906
|
-
if not self.cleaned_data.get('colonel'):
|
|
907
|
-
return self.cleaned_data
|
|
908
|
-
if not self.cleaned_data.get('open_pin'):
|
|
909
|
-
return self.cleaned_data
|
|
910
|
-
if not self.cleaned_data.get('close_pin'):
|
|
911
|
-
return self.cleaned_data
|
|
912
|
-
|
|
913
|
-
self._clean_pin('open_pin')
|
|
914
|
-
self._clean_pin('close_pin')
|
|
915
|
-
|
|
916
|
-
if 'controls' not in self.cleaned_data:
|
|
917
|
-
return self.cleaned_data
|
|
918
|
-
|
|
919
|
-
if len(self.cleaned_data['controls']) not in (0, 2):
|
|
920
|
-
self.add_error('controls', "Must have 0 or 2 controls")
|
|
921
|
-
return self.cleaned_data
|
|
922
|
-
|
|
923
|
-
if len(self.cleaned_data['controls']) == 2:
|
|
924
|
-
method = None
|
|
925
|
-
for c in self.cleaned_data['controls']:
|
|
926
|
-
if not method:
|
|
927
|
-
method = c['method']
|
|
928
|
-
else:
|
|
929
|
-
if c['method'] != method:
|
|
930
|
-
self.add_error('controls', "Both must use the same control method.")
|
|
931
|
-
return self.cleaned_data
|
|
932
|
-
|
|
933
|
-
self._clean_controls()
|
|
934
902
|
|
|
903
|
+
if self.cleaned_data.get('open_pin'):
|
|
904
|
+
self._clean_pin('open_pin')
|
|
905
|
+
if self.cleaned_data.get('close_pin'):
|
|
906
|
+
self._clean_pin('close_pin')
|
|
907
|
+
|
|
908
|
+
if 'controls' in self.cleaned_data:
|
|
909
|
+
if len(self.cleaned_data['controls']) not in (0, 2):
|
|
910
|
+
self.add_error('controls', "Must have 0 or 2 controls")
|
|
911
|
+
return self.cleaned_data
|
|
912
|
+
|
|
913
|
+
if len(self.cleaned_data['controls']) == 2:
|
|
914
|
+
method = None
|
|
915
|
+
for c in self.cleaned_data['controls']:
|
|
916
|
+
if not method:
|
|
917
|
+
method = c['method']
|
|
918
|
+
else:
|
|
919
|
+
if c['method'] != method:
|
|
920
|
+
self.add_error('controls', "Both must use the same control method.")
|
|
921
|
+
return self.cleaned_data
|
|
922
|
+
|
|
923
|
+
self._clean_controls()
|
|
935
924
|
return self.cleaned_data
|
|
936
925
|
|
|
937
926
|
def save(self, commit=True):
|
|
938
|
-
|
|
939
|
-
|
|
927
|
+
if 'open_pin' in self.cleaned_data:
|
|
928
|
+
self.instance.config['open_pin_no'] = \
|
|
929
|
+
self.cleaned_data['open_pin'].no
|
|
930
|
+
if 'close_pin' in self.cleaned_data:
|
|
931
|
+
self.instance.config['close_pin_no'] = \
|
|
932
|
+
self.cleaned_data['close_pin'].no
|
|
940
933
|
return super().save(commit=commit)
|
|
941
934
|
|
|
942
935
|
|
|
@@ -952,9 +945,6 @@ class BurglarSmokeDetectorConfigForm(ColonelComponentForm):
|
|
|
952
945
|
]
|
|
953
946
|
)
|
|
954
947
|
)
|
|
955
|
-
power_action = forms.ChoiceField(
|
|
956
|
-
choices=(('HIGH', "HIGH"), ('LOW', "LOW")),
|
|
957
|
-
)
|
|
958
948
|
sensor_pin = ColonelPinChoiceField(
|
|
959
949
|
queryset=ColonelPin.objects.filter(input=True),
|
|
960
950
|
widget=autocomplete.ListSelect2(
|
|
@@ -966,67 +956,28 @@ class BurglarSmokeDetectorConfigForm(ColonelComponentForm):
|
|
|
966
956
|
]
|
|
967
957
|
)
|
|
968
958
|
)
|
|
969
|
-
sensor_pull = forms.ChoiceField(
|
|
970
|
-
choices=(
|
|
971
|
-
('HIGH', "HIGH"), ('LOW', "LOW"), ("FLOATING", "leave floating"),
|
|
972
|
-
),
|
|
973
|
-
help_text="If you are not sure what is this all about, "
|
|
974
|
-
"you are most definitely want to pull this HIGH or LOW "
|
|
975
|
-
"but not leave it floating!"
|
|
976
|
-
)
|
|
977
959
|
sensor_inverse = forms.TypedChoiceField(
|
|
978
|
-
choices=((
|
|
979
|
-
help_text="Hint: Set
|
|
960
|
+
choices=((0, "No"), (1, "Yes")), coerce=int, initial=0,
|
|
961
|
+
help_text="Hint: Set to Yes, to get ON signal when "
|
|
980
962
|
"you deliver GND to the pin and OFF when you cut it out."
|
|
981
963
|
)
|
|
982
964
|
|
|
983
|
-
|
|
984
965
|
def clean(self):
|
|
985
966
|
super().clean()
|
|
986
|
-
if
|
|
987
|
-
|
|
988
|
-
if '
|
|
989
|
-
|
|
990
|
-
if 'power_pin' not in self.cleaned_data:
|
|
991
|
-
return self.cleaned_data
|
|
992
|
-
|
|
993
|
-
self._clean_pin('sensor_pin')
|
|
994
|
-
self._clean_pin('power_pin')
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
if self.cleaned_data['sensor_pin'].no > 100:
|
|
998
|
-
if self.cleaned_data['sensor_pin'].no < 126:
|
|
999
|
-
if self.cleaned_data.get('sensor_pull') == 'HIGH':
|
|
1000
|
-
self.add_error(
|
|
1001
|
-
'sensor_pull',
|
|
1002
|
-
"Sorry, but this pin is already pulled LOW and "
|
|
1003
|
-
"it can not be changed by this setting. "
|
|
1004
|
-
"Please use 5kohm resistor to physically pull it HIGH "
|
|
1005
|
-
"if that's what you want to do."
|
|
1006
|
-
)
|
|
1007
|
-
else:
|
|
1008
|
-
if self.cleaned_data.get('sensor_pull') == 'LOW':
|
|
1009
|
-
self.add_error(
|
|
1010
|
-
'sensor_pull',
|
|
1011
|
-
"Sorry, but this pin is already pulled HIGH and "
|
|
1012
|
-
"it can not be changed by this setting. "
|
|
1013
|
-
"Please use 5kohm resistor to physically pull it LOW "
|
|
1014
|
-
"if that's what you want to do."
|
|
1015
|
-
)
|
|
1016
|
-
elif self.cleaned_data.get('sensor_pull') != 'FLOATING':
|
|
1017
|
-
if not self.cleaned_data['sensor_pin'].output:
|
|
1018
|
-
self.add_error(
|
|
1019
|
-
'sensor_pin',
|
|
1020
|
-
f"Sorry, but {self.cleaned_data['sensor_pin']} "
|
|
1021
|
-
f"does not have internal pull HIGH/LOW"
|
|
1022
|
-
" resistance capability"
|
|
1023
|
-
)
|
|
967
|
+
if 'sensor_pin' in self.cleaned_data:
|
|
968
|
+
self._clean_pin('sensor_pin')
|
|
969
|
+
if 'power_pin' in self.cleaned_data:
|
|
970
|
+
self._clean_pin('power_pin')
|
|
1024
971
|
|
|
1025
972
|
return self.cleaned_data
|
|
1026
973
|
|
|
1027
974
|
def save(self, commit=True):
|
|
1028
|
-
|
|
1029
|
-
|
|
975
|
+
if 'sensor_pin' in self.cleaned_data:
|
|
976
|
+
self.instance.config['sensor_pin_no'] = \
|
|
977
|
+
self.cleaned_data['sensor_pin'].no
|
|
978
|
+
if 'power_pin' in self.cleaned_data:
|
|
979
|
+
self.instance.config['power_pin_no'] = \
|
|
980
|
+
self.cleaned_data['power_pin'].no
|
|
1030
981
|
return super().save(commit=commit)
|
|
1031
982
|
|
|
1032
983
|
|
|
@@ -1059,18 +1010,14 @@ class TTLockConfigForm(ColonelComponentForm):
|
|
|
1059
1010
|
|
|
1060
1011
|
def save(self, commit=True):
|
|
1061
1012
|
obj = super(ColonelComponentForm, self).save(commit)
|
|
1062
|
-
if commit:
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
self.instance.gateway, self.cleaned_data['door_sensor'],
|
|
1068
|
-
command='watch_lock_sensor'
|
|
1069
|
-
).publish()
|
|
1013
|
+
if commit and 'door_sensor' in self.cleaned_data:
|
|
1014
|
+
GatewayObjectCommand(
|
|
1015
|
+
self.instance.gateway, self.cleaned_data['door_sensor'],
|
|
1016
|
+
command='watch_lock_sensor'
|
|
1017
|
+
).publish()
|
|
1070
1018
|
return obj
|
|
1071
1019
|
|
|
1072
1020
|
|
|
1073
|
-
|
|
1074
1021
|
class DALIDeviceConfigForm(ColonelComponentForm):
|
|
1075
1022
|
interface = ColonelInterfacesChoiceField(
|
|
1076
1023
|
queryset=Interface.objects.filter(type='dali'),
|
|
@@ -1087,7 +1034,9 @@ class DALIDeviceConfigForm(ColonelComponentForm):
|
|
|
1087
1034
|
)
|
|
1088
1035
|
|
|
1089
1036
|
def save(self, commit=True):
|
|
1090
|
-
|
|
1037
|
+
if 'interface' in self.cleaned_data:
|
|
1038
|
+
self.instance.config['dali_interface'] = \
|
|
1039
|
+
self.cleaned_data['interface'].no
|
|
1091
1040
|
return super().save(commit=commit)
|
|
1092
1041
|
|
|
1093
1042
|
|
|
Binary file
|
simo/generic/forms.py
CHANGED
|
@@ -137,8 +137,6 @@ class ThermostatConfigForm(BaseComponentForm):
|
|
|
137
137
|
else:
|
|
138
138
|
self.fields['use_real_feel'].disabled = True
|
|
139
139
|
|
|
140
|
-
|
|
141
|
-
|
|
142
140
|
def save(self, commit=True):
|
|
143
141
|
self.instance.value_units = self.cleaned_data[
|
|
144
142
|
'temperature_sensor'
|
|
@@ -314,8 +312,9 @@ class AlarmGroupConfigForm(BaseComponentForm):
|
|
|
314
312
|
def save(self, *args, **kwargs):
|
|
315
313
|
self.instance.value_units = 'status'
|
|
316
314
|
from .controllers import AlarmGroup
|
|
317
|
-
if
|
|
318
|
-
self.
|
|
315
|
+
if 'is_main' in self.cleaned_data:
|
|
316
|
+
if self.fields['is_main'].widget.attrs.get('disabled'):
|
|
317
|
+
self.cleaned_data['is_main'] = self.fields['is_main'].initial
|
|
319
318
|
obj = super().save(*args, **kwargs)
|
|
320
319
|
if obj.config.get('is_main'):
|
|
321
320
|
for c in Component.objects.filter(
|
|
@@ -366,8 +365,9 @@ class WeatherForecastForm(BaseComponentForm):
|
|
|
366
365
|
def save(self, *args, **kwargs):
|
|
367
366
|
self.instance.value_units = 'status'
|
|
368
367
|
from .controllers import WeatherForecast
|
|
369
|
-
if self.fields
|
|
370
|
-
self.
|
|
368
|
+
if 'is_main' in self.fields and 'is_main' in self.cleaned_data:
|
|
369
|
+
if self.fields['is_main'].widget.attrs.get('disabled'):
|
|
370
|
+
self.cleaned_data['is_main'] = self.fields['is_main'].initial
|
|
371
371
|
obj = super().save(*args, **kwargs)
|
|
372
372
|
if obj.config.get('is_main'):
|
|
373
373
|
for c in Component.objects.filter(
|
|
@@ -541,11 +541,12 @@ class WateringConfigForm(BaseComponentForm):
|
|
|
541
541
|
return contours
|
|
542
542
|
|
|
543
543
|
def save(self, commit=True):
|
|
544
|
-
|
|
545
|
-
self.
|
|
546
|
-
|
|
544
|
+
if 'contours' in self.cleaned_data:
|
|
545
|
+
self.instance.config['program'] = self.controller._build_program(
|
|
546
|
+
self.cleaned_data['contours']
|
|
547
|
+
)
|
|
547
548
|
obj = super().save(commit=commit)
|
|
548
|
-
if commit:
|
|
549
|
+
if commit and 'contours' in self.cleaned_data:
|
|
549
550
|
obj.slaves.clear()
|
|
550
551
|
for contour in self.cleaned_data['contours']:
|
|
551
552
|
obj.slaves.add(
|
|
@@ -565,10 +566,6 @@ class StateForm(forms.Form):
|
|
|
565
566
|
help_text = forms.CharField(required=False, widget=forms.Textarea(attrs={'rows': 3}))
|
|
566
567
|
prefix = 'states'
|
|
567
568
|
|
|
568
|
-
def clean(self):
|
|
569
|
-
print("Let's clean the data! ", self.cleaned_data)
|
|
570
|
-
return self.cleaned_data
|
|
571
|
-
|
|
572
569
|
|
|
573
570
|
class StateSelectForm(BaseComponentForm):
|
|
574
571
|
states = FormsetField(
|
|
@@ -602,18 +599,20 @@ class AlarmClockEventForm(forms.Form):
|
|
|
602
599
|
if not self.cleaned_data.get('play_action'):
|
|
603
600
|
return self.cleaned_data
|
|
604
601
|
component = self.cleaned_data.get('component')
|
|
605
|
-
if
|
|
606
|
-
self.
|
|
607
|
-
'play_action',
|
|
608
|
-
f"{component} has no {self.cleaned_data['play_action']} action!"
|
|
609
|
-
)
|
|
610
|
-
if self.cleaned_data.get('reverse_action'):
|
|
611
|
-
if not hasattr(component, self.cleaned_data['reverse_action']):
|
|
602
|
+
if 'play_action' in self.cleaned_data:
|
|
603
|
+
if not hasattr(component, self.cleaned_data['play_action']):
|
|
612
604
|
self.add_error(
|
|
613
|
-
'
|
|
614
|
-
f"{component} has no "
|
|
615
|
-
f"{self.cleaned_data['reverse_action']} action!"
|
|
605
|
+
'play_action',
|
|
606
|
+
f"{component} has no {self.cleaned_data['play_action']} action!"
|
|
616
607
|
)
|
|
608
|
+
if 'reverse_action' in self.cleaned_data:
|
|
609
|
+
if self.cleaned_data.get('reverse_action'):
|
|
610
|
+
if not hasattr(component, self.cleaned_data['reverse_action']):
|
|
611
|
+
self.add_error(
|
|
612
|
+
'reverse_action',
|
|
613
|
+
f"{component} has no "
|
|
614
|
+
f"{self.cleaned_data['reverse_action']} action!"
|
|
615
|
+
)
|
|
617
616
|
return self.cleaned_data
|
|
618
617
|
|
|
619
618
|
|
|
@@ -633,7 +632,7 @@ class AlarmClockConfigForm(BaseComponentForm):
|
|
|
633
632
|
|
|
634
633
|
def save(self, commit=True):
|
|
635
634
|
obj = super().save(commit=commit)
|
|
636
|
-
if commit:
|
|
635
|
+
if commit and 'default_events' in self.cleaned_data:
|
|
637
636
|
obj.slaves.clear()
|
|
638
637
|
for comp in self.cleaned_data['default_events']:
|
|
639
638
|
c = Component.objects.filter(pk=comp['component']).first()
|
|
Binary file
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Generated by Django 3.2.9 on 2024-05-06 08:34
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
('users', '0026_fingerprint_name'),
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
operations = [
|
|
13
|
+
migrations.AddField(
|
|
14
|
+
model_name='permissionsrole',
|
|
15
|
+
name='can_manage_components',
|
|
16
|
+
field=models.BooleanField(default=False, help_text='Can manage zones and basic component parameters via SIMO.io app.'),
|
|
17
|
+
),
|
|
18
|
+
]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Generated by Django 3.2.9 on 2024-05-06 11:46
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
('users', '0027_permissionsrole_can_manage_components'),
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
operations = [
|
|
13
|
+
migrations.RemoveField(
|
|
14
|
+
model_name='permissionsrole',
|
|
15
|
+
name='can_manage_components',
|
|
16
|
+
),
|
|
17
|
+
migrations.AddField(
|
|
18
|
+
model_name='permissionsrole',
|
|
19
|
+
name='is_owner',
|
|
20
|
+
field=models.BooleanField(default=False, help_text='Can manage zones, basic component parametersand other things via SIMO.io app, but is not yet allowed to perform any serious system changes, like superusers can.'),
|
|
21
|
+
),
|
|
22
|
+
]
|
|
Binary file
|
|
Binary file
|
simo/users/models.py
CHANGED
|
@@ -35,6 +35,12 @@ class PermissionsRole(models.Model):
|
|
|
35
35
|
help_text="Global role if instance is not set."
|
|
36
36
|
)
|
|
37
37
|
name = models.CharField(max_length=100, db_index=True)
|
|
38
|
+
is_owner = models.BooleanField(
|
|
39
|
+
default=False,
|
|
40
|
+
help_text="Can manage zones, basic component parameters"
|
|
41
|
+
"and other things via SIMO.io app, but is not yet allowed "
|
|
42
|
+
"to perform any serious system changes, like superusers can."
|
|
43
|
+
)
|
|
38
44
|
can_manage_users = models.BooleanField(default=False)
|
|
39
45
|
is_superuser = models.BooleanField(
|
|
40
46
|
default=False,
|
|
@@ -213,12 +219,7 @@ class User(AbstractBaseUser, SimoAdminMixin):
|
|
|
213
219
|
return self.is_active and self.ssh_key and self.is_master
|
|
214
220
|
|
|
215
221
|
def get_role(self, instance):
|
|
216
|
-
|
|
217
|
-
if not role.instance:
|
|
218
|
-
return role
|
|
219
|
-
for role in self.roles.all():
|
|
220
|
-
if role.instance == instance:
|
|
221
|
-
return role
|
|
222
|
+
return self.roles.filter(instance=instance).first()
|
|
222
223
|
|
|
223
224
|
def set_instance(self, instance):
|
|
224
225
|
self._instance = instance
|
|
@@ -26,7 +26,7 @@ simo/_hub_template/hub/supervisor.conf,sha256=IY3fdK0fDD2eAothB0n54xhjQj8LYoXIR9
|
|
|
26
26
|
simo/_hub_template/hub/urls.py,sha256=Ydm-1BkYAzWeEF-MKSDIFf-7aE4qNLPm48-SA51XgJQ,25
|
|
27
27
|
simo/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
28
28
|
simo/core/admin.py,sha256=vZp2TZTt41YararDkIEs35X6ARqRp94wrIddsIfe-Bs,17738
|
|
29
|
-
simo/core/api.py,sha256=
|
|
29
|
+
simo/core/api.py,sha256=YLO6r1htHgBjokAHoTXCtEFmx_Ug5xIlPg6sQX-CT4A,23413
|
|
30
30
|
simo/core/api_auth.py,sha256=_3hG4e1eLKrcRnSAOB_xTL6cwtOJ2_7JS7GZU_iqTgA,1251
|
|
31
31
|
simo/core/api_meta.py,sha256=ySmmhtVrWatI3yqnYPuP5ipapmJfyfEbl32w-7_W5O4,3551
|
|
32
32
|
simo/core/app_widgets.py,sha256=EEQOto3fGR0syDqpJE38tQrx8DoTTyg26nF5kYzHY38,2018
|
|
@@ -38,15 +38,15 @@ simo/core/controllers.py,sha256=iJ7cIGv2WhSGjyxCFfK49pZXuEMfSe75bMEAkRN8G4g,2710
|
|
|
38
38
|
simo/core/dynamic_settings.py,sha256=U2WNL96JzVXdZh0EqMPWrxqO6BaRR2Eo5KTDqz7MC4o,1943
|
|
39
39
|
simo/core/events.py,sha256=LvtonJGNyCb6HLozs4EG0WZItnDwNdtnGQ4vTcnKvUs,4438
|
|
40
40
|
simo/core/filters.py,sha256=ghtOZcrwNAkIyF5_G9Sn73NkiI71mXv0NhwCk4IyMIM,411
|
|
41
|
-
simo/core/forms.py,sha256=
|
|
41
|
+
simo/core/forms.py,sha256=tsWBsoBJeUiXDVU6lKbMbnBZORmNrnK5XnoBS_9uWsU,23583
|
|
42
42
|
simo/core/gateways.py,sha256=s_c2W0v2_te89i6LS4Nj7F2wn9UwjZXPT7pfy6SToVo,3714
|
|
43
43
|
simo/core/loggers.py,sha256=EBdq23gTQScVfQVH-xeP90-wII2DQFDjoROAW6ggUP4,1645
|
|
44
44
|
simo/core/managers.py,sha256=WoQ4OX3akIvoroSYji-nLVqXBSJzCiC1u_IiWkKbKmA,2413
|
|
45
45
|
simo/core/middleware.py,sha256=pO52hQOJV_JRmNyUe7zfufSnJFlRITOWX6jwkoPWJhk,2052
|
|
46
46
|
simo/core/models.py,sha256=m05fXzOtLrq4LEGknU1mjTHzaTmIEBi1zhwvtDWDknE,19773
|
|
47
|
-
simo/core/permissions.py,sha256=
|
|
47
|
+
simo/core/permissions.py,sha256=TCIHf6kjVAs2mGQ2IV2YRxjHYBsDbl47FLm_wCRKMwY,2618
|
|
48
48
|
simo/core/routing.py,sha256=X1_IHxyA-_Q7hw1udDoviVP4_FSBDl8GYETTC2zWTbY,499
|
|
49
|
-
simo/core/serializers.py,sha256=
|
|
49
|
+
simo/core/serializers.py,sha256=xL0RHvi20_2qsQzHMN92xhmpp-wmo54UUl9wfkYp61k,18329
|
|
50
50
|
simo/core/signal_receivers.py,sha256=EZ8NSYZxUgSaLS16YZdK7T__l8dl0joMRllOxx5PUt4,2809
|
|
51
51
|
simo/core/socket_consumers.py,sha256=n7VE2Fvqt4iEAYLTRbTPOcI-7tszMAADu7gimBxB-Fg,9635
|
|
52
52
|
simo/core/storage.py,sha256=YlxmdRs-zhShWtFKgpJ0qp2NDBuIkJGYC1OJzqkbttQ,572
|
|
@@ -57,7 +57,7 @@ simo/core/views.py,sha256=hlAKpAbCbqI3a-uL5tDp532T2oLFiF0MBzKUJ_SNzo0,5833
|
|
|
57
57
|
simo/core/widgets.py,sha256=J9e06C6I22F6xKic3VMgG7WeX07glAcl-4bF2Mg180A,2827
|
|
58
58
|
simo/core/__pycache__/__init__.cpython-38.pyc,sha256=y0IW37wBUIGa3Eh_ZG28pRqHKoLiPyTgUX2OnbkEPlc,158
|
|
59
59
|
simo/core/__pycache__/admin.cpython-38.pyc,sha256=gZpNwM9f-JMbGe9sbQKt2hU2BBYhRzKSH9XRJYKN3Vo,13534
|
|
60
|
-
simo/core/__pycache__/api.cpython-38.pyc,sha256=
|
|
60
|
+
simo/core/__pycache__/api.cpython-38.pyc,sha256=yL3vQiZ6McrOILD37EmuRdzZHZ0bPCsboDAeHZP_iyM,18581
|
|
61
61
|
simo/core/__pycache__/api_auth.cpython-38.pyc,sha256=5UTBr3rDMERAfc0OuOVDwGeQkt6Q7GLBtZJAMBse1sg,1712
|
|
62
62
|
simo/core/__pycache__/api_meta.cpython-38.pyc,sha256=94T3_rybn2T1_bkaDQnQRyjy21LBaGOnz-mmkJ6T0N8,2840
|
|
63
63
|
simo/core/__pycache__/app_widgets.cpython-38.pyc,sha256=9Es2wZNduzUJv-jZ_HX0-L3vqwpXWBbseEwoC5K6b-w,3465
|
|
@@ -69,15 +69,15 @@ simo/core/__pycache__/controllers.cpython-38.pyc,sha256=40wJ3mu0CnzexxDzXyDIlSxa
|
|
|
69
69
|
simo/core/__pycache__/dynamic_settings.cpython-38.pyc,sha256=ELu06Hub4DOidja71ybvD3ZM4HdXiyZjNJrZfnXZXNA,2476
|
|
70
70
|
simo/core/__pycache__/events.cpython-38.pyc,sha256=A1Axx-qftd1r7st7wkO3DkvTdt9-RkcJe5KJhpzJVk8,5109
|
|
71
71
|
simo/core/__pycache__/filters.cpython-38.pyc,sha256=VIMADCBiYhziIyRmxAyUDJluZvuZmiC4bNYWTRsGSao,721
|
|
72
|
-
simo/core/__pycache__/forms.cpython-38.pyc,sha256=
|
|
72
|
+
simo/core/__pycache__/forms.cpython-38.pyc,sha256=d0ruviTG6zihcc9hJ1TvVxPODwe7LJCyo_ZpLIUt5pI,19850
|
|
73
73
|
simo/core/__pycache__/gateways.cpython-38.pyc,sha256=XBiwMfBkjoQ2re6jvADJOwK0_0Aav-crzie9qtfqT9U,4599
|
|
74
74
|
simo/core/__pycache__/loggers.cpython-38.pyc,sha256=Z-cdQnC6XlIonPV4Sl4E52tP4NMEdPAiHK0cFaIL7I8,1623
|
|
75
75
|
simo/core/__pycache__/managers.cpython-38.pyc,sha256=5vstOMfm997CZBBkaSiaS7EojhLTWZlbeA_EQ8u-yfg,2554
|
|
76
76
|
simo/core/__pycache__/middleware.cpython-38.pyc,sha256=ESR5JPtITo9flczO0672sfzYUxrc_cQU0e0w5DFL-60,2038
|
|
77
77
|
simo/core/__pycache__/models.cpython-38.pyc,sha256=kYcojazryAAgZCXMoqqutIG2p8ofSKxza9TMx1SR2dQ,17233
|
|
78
|
-
simo/core/__pycache__/permissions.cpython-38.pyc,sha256=
|
|
78
|
+
simo/core/__pycache__/permissions.cpython-38.pyc,sha256=SV8hZn3Zh-K4NrdtADZ9RlkkHSHTDbScR24Nto8mJXw,2759
|
|
79
79
|
simo/core/__pycache__/routing.cpython-38.pyc,sha256=3T3FPJ8Cn99xZCGvMyg2xjl7al-Shm9CelbSpkJtNP8,599
|
|
80
|
-
simo/core/__pycache__/serializers.cpython-38.pyc,sha256=
|
|
80
|
+
simo/core/__pycache__/serializers.cpython-38.pyc,sha256=LqjUor0ggXAWSRgTSEdWYGllIOhio8Mlh-kkhBbai30,17237
|
|
81
81
|
simo/core/__pycache__/signal_receivers.cpython-38.pyc,sha256=sgjH_wv-1U99auH5uHb3or0qettPeHAlsz8P7B03ajY,2430
|
|
82
82
|
simo/core/__pycache__/socket_consumers.cpython-38.pyc,sha256=NJUr7nRyHFvmAumxxWpsod5wzVVZM99rCEuJs1utHA4,8432
|
|
83
83
|
simo/core/__pycache__/storage.cpython-38.pyc,sha256=BTkYH8QQyjqI0WOtJC8fHNtgu0YA1vjqZclXjC2vCVI,1116
|
|
@@ -173,6 +173,7 @@ simo/core/migrations/0028_rename_subcomponents_component_slaves.py,sha256=ioQcfO
|
|
|
173
173
|
simo/core/migrations/0029_auto_20240229_1331.py,sha256=BYXPNwjXApAx7mxE5li3QssfksWTsSjDf_VPQ8iGV8c,1140
|
|
174
174
|
simo/core/migrations/0030_alter_instance_timezone.py,sha256=XZuYr2eD9MvE21Jxfdat8RC3sbc7PaGUfNPaqqxBNzE,23248
|
|
175
175
|
simo/core/migrations/0031_auto_20240429_1231.py,sha256=kskD8dylxqg-l-ZQgxl6ZdZd7iNcJ52rOGPJFa9s-wk,829
|
|
176
|
+
simo/core/migrations/0032_auto_20240506_0834.py,sha256=w-dXSZVYZD0B6pRmMXLy27Y2twRR3WbN9WdNJCQqqwk,746
|
|
176
177
|
simo/core/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
177
178
|
simo/core/migrations/__pycache__/0001_initial.cpython-38.pyc,sha256=w6GiBXVxWj30Bg4Sn_pFVeA041d-pCrkaq8mR3KuF70,5381
|
|
178
179
|
simo/core/migrations/__pycache__/0002_load_icons.cpython-38.pyc,sha256=Nb9RrPjVYo_RpZ5PmzoaEIWGCeVt4kicpmGiKlBrpIw,2123
|
|
@@ -205,6 +206,7 @@ simo/core/migrations/__pycache__/0028_rename_subcomponents_component_slaves.cpyt
|
|
|
205
206
|
simo/core/migrations/__pycache__/0029_auto_20240229_1331.cpython-38.pyc,sha256=nO6lBU-1SxMQv67jQs7cGgNnot722UVL9pj3UE14I1k,1086
|
|
206
207
|
simo/core/migrations/__pycache__/0030_alter_instance_timezone.cpython-38.pyc,sha256=paRgdijg5o8QzluT4GjSEQIMk6UAF7XmSwjqknXPE_4,16333
|
|
207
208
|
simo/core/migrations/__pycache__/0031_auto_20240429_1231.cpython-38.pyc,sha256=Kl76gU1VUyTWMz9RIZUrbnb_xeteWLDSRfs_GtNCVow,863
|
|
209
|
+
simo/core/migrations/__pycache__/0032_auto_20240506_0834.cpython-38.pyc,sha256=UhrErBBhvAOPVph1x3ibQvSrcVUrITIsU9CGmjT8nLk,895
|
|
208
210
|
simo/core/migrations/__pycache__/__init__.cpython-38.pyc,sha256=VZmDQ57BTcebuM0KMhjiTOabgWZCBxQmSJzWZos9SO8,169
|
|
209
211
|
simo/core/static/ansi_styles.css,sha256=4ieJGrjZPKyPSago9FdB_gflHoGE1vxCHi8qVn5tY-Y,37352
|
|
210
212
|
simo/core/static/admin/Img/plus.svg,sha256=2NpSFPWqGIjpAQGFI7LDQHPKagEhYkJiJX95ufCoZaI,741
|
|
@@ -10173,7 +10175,7 @@ simo/fleet/auto_urls.py,sha256=X04oKJWA48wFW5iXg3PPROY2KDdHn_a99orQSE28QC4,518
|
|
|
10173
10175
|
simo/fleet/base_types.py,sha256=wL9RVkHr0gA7HI1wZq0pruGEIgvQqpfnCL4cC3ywsvw,102
|
|
10174
10176
|
simo/fleet/ble.py,sha256=eHA_9ABjbmH1vUVCv9hiPXQL2GZZSEVwfO0xyI1S0nI,1081
|
|
10175
10177
|
simo/fleet/controllers.py,sha256=rTxRFf-LKWAZxzixrsLZHHm51BmMx9a1PLdgf6inlNM,20533
|
|
10176
|
-
simo/fleet/forms.py,sha256=
|
|
10178
|
+
simo/fleet/forms.py,sha256=ivP30jRtABtf1NkAuOaRRwZB7khHIJ7XwEzS5aCF480,36881
|
|
10177
10179
|
simo/fleet/gateways.py,sha256=KV5i5fxXIrlK-k6zyEkk83x11GJt-ELQ0npb4Ac83cM,3693
|
|
10178
10180
|
simo/fleet/managers.py,sha256=XOpDOA9L-f_550TNSyXnJbun2EmtGz1TenVTMlUSb8E,807
|
|
10179
10181
|
simo/fleet/models.py,sha256=ve-97F1cwGt-AmwfSJK0d-57pP3NyZpeu0XlHu2oK28,14494
|
|
@@ -10190,7 +10192,7 @@ simo/fleet/__pycache__/auto_urls.cpython-38.pyc,sha256=SqyTuaz_kEBvx-bL46SclsZEE
|
|
|
10190
10192
|
simo/fleet/__pycache__/base_types.cpython-38.pyc,sha256=deyPwjpT6xZiFxBGFnj5b7R-lbdOTh2krgpJhrcGVhc,274
|
|
10191
10193
|
simo/fleet/__pycache__/ble.cpython-38.pyc,sha256=Nrof9w7cm4OlpFWHeVnmvvanh2_oF9oQ3TknJiV93-0,1267
|
|
10192
10194
|
simo/fleet/__pycache__/controllers.cpython-38.pyc,sha256=l9bz18Qp33C12TJOKPSn9vIXnlBKnBusODNk7Fg64qA,18103
|
|
10193
|
-
simo/fleet/__pycache__/forms.cpython-38.pyc,sha256=
|
|
10195
|
+
simo/fleet/__pycache__/forms.cpython-38.pyc,sha256=TH4NC-otAl9V6YlvrffE9sghlT-tw_3RFK1ztO-fzfg,27368
|
|
10194
10196
|
simo/fleet/__pycache__/gateways.cpython-38.pyc,sha256=YAcgTOqJbtjGI03lvEcU6keFfrwAHkObVmErYzfRvjk,3569
|
|
10195
10197
|
simo/fleet/__pycache__/managers.cpython-38.pyc,sha256=8uz-xpUiqbGDgXIZ_XRZtFb-Tju6NGxflGg-Ee4Yo6k,1310
|
|
10196
10198
|
simo/fleet/__pycache__/models.cpython-38.pyc,sha256=pHNRUiPRjH0SLp14pzbSIxHi_-27SpZFgSh_7lzA8Wo,12359
|
|
@@ -10274,7 +10276,7 @@ simo/generic/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
10274
10276
|
simo/generic/app_widgets.py,sha256=E_pnpA1hxMIhenRCrHoQ5cik06jm2BAHCkl_eo-OudU,1264
|
|
10275
10277
|
simo/generic/base_types.py,sha256=djymox_boXTHX1BTTCLXrCH7ED-uAsV_idhaDOc3OLI,409
|
|
10276
10278
|
simo/generic/controllers.py,sha256=WYuOUzDWvkYRaTvlbdGy_qmwp1o_ohqKDfV7OrOq2QU,52218
|
|
10277
|
-
simo/generic/forms.py,sha256=
|
|
10279
|
+
simo/generic/forms.py,sha256=uLZ25Fw1xGqyDBngWPve9EaHpyldFegY_TPBKV4Oby0,24051
|
|
10278
10280
|
simo/generic/gateways.py,sha256=b3tQ2bAkDVYXCF5iZi2yi-6nZAM8WmHE9ICwxMyR0to,17034
|
|
10279
10281
|
simo/generic/models.py,sha256=ni1cgqVZ8ttk1snW26EB-2dqsHRZkLg6463my_Cu3EU,6618
|
|
10280
10282
|
simo/generic/routing.py,sha256=elQVZmgnPiieEuti4sJ7zITk1hlRxpgbotcutJJgC60,228
|
|
@@ -10283,7 +10285,7 @@ simo/generic/__pycache__/__init__.cpython-38.pyc,sha256=mLu54WS9KIl-pHwVCBKpsDFI
|
|
|
10283
10285
|
simo/generic/__pycache__/app_widgets.cpython-38.pyc,sha256=0IoKRG9n1tkNRRkrqAeOQwWBPd_33u98JBcVtMVVCio,2374
|
|
10284
10286
|
simo/generic/__pycache__/base_types.cpython-38.pyc,sha256=ptw6axyAqemZA35oa6vzr7EihzvbhW9w7Y-G6kfDedU,555
|
|
10285
10287
|
simo/generic/__pycache__/controllers.cpython-38.pyc,sha256=e0bvgyePgJbIs1omBq0TRPlVSKar2sK_JbUKqDRj7mY,33235
|
|
10286
|
-
simo/generic/__pycache__/forms.cpython-38.pyc,sha256=
|
|
10288
|
+
simo/generic/__pycache__/forms.cpython-38.pyc,sha256=U67jiwt6R8J547n7d84-_9mUxCXxKl-L0VFdYfo1aiM,17787
|
|
10287
10289
|
simo/generic/__pycache__/gateways.cpython-38.pyc,sha256=a4lLIMPyxm9tNzIqorXHIPZFVTcXlPsM1ycJMghxcHA,12673
|
|
10288
10290
|
simo/generic/__pycache__/models.cpython-38.pyc,sha256=yr64EX5n9u9uaOHNbPhNIBoBJo4Y3V_lvDj1ggMAZCQ,5123
|
|
10289
10291
|
simo/generic/__pycache__/routing.cpython-38.pyc,sha256=xtxTUTBTdivzFyA5Wh7k-hUj1WDO_FiRq6HYXdbr9Ks,382
|
|
@@ -10365,7 +10367,7 @@ simo/users/auth_backends.py,sha256=I5pnaTa20-Lxfw_dFG8471xDITb0_fQl1PVhJalp5vU,3
|
|
|
10365
10367
|
simo/users/auto_urls.py,sha256=lcJvteBsbHQMJieZpDz-63tDYejLApqsW3CUnDakd7k,272
|
|
10366
10368
|
simo/users/dynamic_settings.py,sha256=sEIsi4yJw3kH46Jq_aOkSuK7QTfQACGUE-lkyBogCaM,570
|
|
10367
10369
|
simo/users/middleware.py,sha256=GMCrnWSc_2qCleyQIkfQGdL-pU-UTEcSg1wPvIKZ9uk,1210
|
|
10368
|
-
simo/users/models.py,sha256=
|
|
10370
|
+
simo/users/models.py,sha256=VJWjqDXZpCzsrTYeXmxc8JrsxmK0Ml9-X9eOHj4BP78,19005
|
|
10369
10371
|
simo/users/permissions.py,sha256=IwtYS8yQdupWbYKR9VimSRDV3qCJ2jXP57Lyjpb2EQM,242
|
|
10370
10372
|
simo/users/serializers.py,sha256=e6yIUsO7BfvrZ4IQHBn-FdpAUMgic5clmGQdTtRlGRY,2515
|
|
10371
10373
|
simo/users/sso_urls.py,sha256=gQOaPvGMYFD0NCVSwyoWO-mTEHe5j9sbzV_RK7kdvp0,251
|
|
@@ -10380,7 +10382,7 @@ simo/users/__pycache__/auth_backends.cpython-38.pyc,sha256=MuOieBIXt6lrDx83-UQtd
|
|
|
10380
10382
|
simo/users/__pycache__/auto_urls.cpython-38.pyc,sha256=K-3sz2h-cEitoflSmZk1t0eUg5mQMMGLNZFREVwG7_o,430
|
|
10381
10383
|
simo/users/__pycache__/dynamic_settings.cpython-38.pyc,sha256=6F8JBjZkHykySnmZjNEzjS0ijbmPdcp9yUAZ5kqq_Fo,864
|
|
10382
10384
|
simo/users/__pycache__/middleware.cpython-38.pyc,sha256=Tj4nVEAvxEW3xA63fBRiJWRJpz_M848ZOqbHioc_IPE,1149
|
|
10383
|
-
simo/users/__pycache__/models.cpython-38.pyc,sha256=
|
|
10385
|
+
simo/users/__pycache__/models.cpython-38.pyc,sha256=Hui7Uu0kCIENoQuDZ5xoCHVNqsoI_hygaxKAgqqyPo8,17739
|
|
10384
10386
|
simo/users/__pycache__/permissions.cpython-38.pyc,sha256=ez5NxoL_JUeeH6GsKhvFreuA3FCBgGf9floSypdXUtM,633
|
|
10385
10387
|
simo/users/__pycache__/serializers.cpython-38.pyc,sha256=ylapsfu5qbSzbfX2lG3uc4wV6hhndFbIvI109lhhKOo,3461
|
|
10386
10388
|
simo/users/__pycache__/sso_urls.cpython-38.pyc,sha256=uAwDozpOmrhUald-8tOHANILXkH7-TI8fNYXOtPkSY8,402
|
|
@@ -10414,6 +10416,8 @@ simo/users/migrations/0023_auto_20240105_0719.py,sha256=OAkWJusXjzT6dx4EgwjvNvME
|
|
|
10414
10416
|
simo/users/migrations/0024_fingerprint.py,sha256=0wfplJ3iHv_6heJx7yIQYX3D68Nf9pLPlIZoM5NcPk8,1021
|
|
10415
10417
|
simo/users/migrations/0025_rename_name_fingerprint_type_and_more.py,sha256=Azw_a1qxIDttdG4m0DcLa82amv-mFsbW8PsjG9qFL0Y,466
|
|
10416
10418
|
simo/users/migrations/0026_fingerprint_name.py,sha256=DPmfi1brbaPymdNiPgc7dINSKy97yVHdKpKp-ZfnS3I,428
|
|
10419
|
+
simo/users/migrations/0027_permissionsrole_can_manage_components.py,sha256=VcGZE6u-q6UkGo7D01K_T1XBtIvIGe8SCk5ZPRrPpGo,485
|
|
10420
|
+
simo/users/migrations/0028_auto_20240506_1146.py,sha256=7RUFF2rJH-bnPeHwc77p8Q4kEAc3owyG4qp9Kc4aKhU,716
|
|
10417
10421
|
simo/users/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10418
10422
|
simo/users/migrations/__pycache__/0001_initial.cpython-38.pyc,sha256=ngXA1QR-Qc2VS-BTTZWybVXiEfifIgKaVS6bADiN8nU,4269
|
|
10419
10423
|
simo/users/migrations/__pycache__/0002_componentpermission.cpython-38.pyc,sha256=pknJnpic8p6Vdx9DX41FfODXNnvexDswJtUCmC5w1tg,995
|
|
@@ -10441,6 +10445,8 @@ simo/users/migrations/__pycache__/0023_auto_20240105_0719.cpython-38.pyc,sha256=
|
|
|
10441
10445
|
simo/users/migrations/__pycache__/0024_fingerprint.cpython-38.pyc,sha256=QLW3v4BK78HL682qeHkK_aXZgCJSs9sIIte60ieF5ZY,1132
|
|
10442
10446
|
simo/users/migrations/__pycache__/0025_rename_name_fingerprint_type_and_more.cpython-38.pyc,sha256=21CgVotrTT02MW58zqePt_-gsbpdN_m01T_SOHgWkBo,631
|
|
10443
10447
|
simo/users/migrations/__pycache__/0026_fingerprint_name.cpython-38.pyc,sha256=Ti0NLIKb0Wffn33LCBQQ-cumFCX6JFxSi1FYoV8C0ZE,642
|
|
10448
|
+
simo/users/migrations/__pycache__/0027_permissionsrole_can_manage_components.cpython-38.pyc,sha256=Ju4FLSKUNoJ419bAp_Np_MKrSyxzlJnYGfeNYg7HC5M,721
|
|
10449
|
+
simo/users/migrations/__pycache__/0028_auto_20240506_1146.cpython-38.pyc,sha256=e8J_lTYJMixdqV37OgonG9ndd9gjn1E9hVj-jh4bxGw,874
|
|
10444
10450
|
simo/users/migrations/__pycache__/__init__.cpython-38.pyc,sha256=NKq7WLgktK8WV1oOqCPbAbdkrPV5GRGhYx4VxxI4dcs,170
|
|
10445
10451
|
simo/users/templates/conf/mosquitto.conf,sha256=1eIGNuRu4Y3hfAU6qiWix648eCRrw0oOT24PnyFI4ys,189
|
|
10446
10452
|
simo/users/templates/conf/mosquitto_acls.conf,sha256=ga44caTDNQE0CBKw55iM2jOuna6-9fKGwAhjyERZdRE,500
|
|
@@ -10450,8 +10456,8 @@ simo/users/templates/invitations/expired_msg.html,sha256=47DEQpj8HBSa-_TImW-5JCe
|
|
|
10450
10456
|
simo/users/templates/invitations/expired_suggestion.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10451
10457
|
simo/users/templates/invitations/taken_msg.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10452
10458
|
simo/users/templates/invitations/taken_suggestion.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10453
|
-
simo-2.0.
|
|
10454
|
-
simo-2.0.
|
|
10455
|
-
simo-2.0.
|
|
10456
|
-
simo-2.0.
|
|
10457
|
-
simo-2.0.
|
|
10459
|
+
simo-2.0.26.dist-info/LICENSE.md,sha256=M7wm1EmMGDtwPRdg7kW4d00h1uAXjKOT3HFScYQMeiE,34916
|
|
10460
|
+
simo-2.0.26.dist-info/METADATA,sha256=7QlteBUjfDoMYB7h3GQspaQINFPXrri9o-vxvt9QPQY,1730
|
|
10461
|
+
simo-2.0.26.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
10462
|
+
simo-2.0.26.dist-info/top_level.txt,sha256=GmS1hrAbpVqn9OWZh6UX82eIOdRLgYA82RG9fe8v4Rs,5
|
|
10463
|
+
simo-2.0.26.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|