simo 2.3.7__py3-none-any.whl → 2.4.2__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/__pycache__/settings.cpython-38.pyc +0 -0
- simo/backups/__pycache__/admin.cpython-38.pyc +0 -0
- simo/backups/__pycache__/dynamic_settings.cpython-38.pyc +0 -0
- simo/backups/__pycache__/models.cpython-38.pyc +0 -0
- simo/backups/__pycache__/tasks.cpython-38.pyc +0 -0
- simo/backups/admin.py +64 -3
- simo/backups/dynamic_settings.py +0 -7
- simo/backups/migrations/0002_backuplog_backup_level_backup_size.py +32 -0
- simo/backups/migrations/0003_alter_backuplog_options_alter_backup_size.py +22 -0
- simo/backups/migrations/0004_alter_backup_options_alter_backuplog_options_and_more.py +29 -0
- simo/backups/migrations/__pycache__/0002_backuplog_backup_level_backup_size.cpython-38.pyc +0 -0
- simo/backups/migrations/__pycache__/0003_alter_backuplog_options_alter_backup_size.cpython-38.pyc +0 -0
- simo/backups/migrations/__pycache__/0004_alter_backup_options_alter_backuplog_options_and_more.cpython-38.pyc +0 -0
- simo/backups/models.py +9 -2
- simo/backups/tasks.py +255 -113
- simo/core/__pycache__/api.cpython-38.pyc +0 -0
- simo/core/__pycache__/context.cpython-38.pyc +0 -0
- simo/core/__pycache__/controllers.cpython-38.pyc +0 -0
- simo/core/__pycache__/models.cpython-38.pyc +0 -0
- simo/core/__pycache__/tasks.cpython-38.pyc +0 -0
- simo/core/__pycache__/views.cpython-38.pyc +0 -0
- simo/core/api.py +2 -7
- simo/core/controllers.py +8 -1
- simo/core/management/_hub_template/hub/supervisor.conf +4 -0
- simo/core/migrations/0042_alter_instance_timezone.py +18 -0
- simo/core/migrations/__pycache__/0042_alter_instance_timezone.cpython-38.pyc +0 -0
- simo/core/models.py +6 -2
- simo/core/tasks.py +8 -1
- simo/core/views.py +4 -12
- simo/fleet/__pycache__/forms.cpython-38.pyc +0 -0
- simo/fleet/forms.py +140 -176
- simo/fleet/migrations/0038_alter_colonel_type.py +18 -0
- simo/fleet/migrations/__pycache__/0038_alter_colonel_type.cpython-38.pyc +0 -0
- simo/notifications/__pycache__/models.cpython-38.pyc +0 -0
- simo/settings.py +1 -0
- simo/users/__pycache__/models.cpython-38.pyc +0 -0
- simo/users/__pycache__/serializers.cpython-38.pyc +0 -0
- simo/users/migrations/0033_alter_user_ssh_key.py +18 -0
- simo/users/migrations/__pycache__/0033_alter_user_ssh_key.cpython-38.pyc +0 -0
- simo/users/models.py +38 -16
- {simo-2.3.7.dist-info → simo-2.4.2.dist-info}/METADATA +2 -1
- {simo-2.3.7.dist-info → simo-2.4.2.dist-info}/RECORD +46 -33
- {simo-2.3.7.dist-info → simo-2.4.2.dist-info}/LICENSE.md +0 -0
- {simo-2.3.7.dist-info → simo-2.4.2.dist-info}/WHEEL +0 -0
- {simo-2.3.7.dist-info → simo-2.4.2.dist-info}/entry_points.txt +0 -0
- {simo-2.3.7.dist-info → simo-2.4.2.dist-info}/top_level.txt +0 -0
simo/fleet/forms.py
CHANGED
|
@@ -195,17 +195,15 @@ class ControlForm(forms.Form):
|
|
|
195
195
|
|
|
196
196
|
|
|
197
197
|
class ColonelBinarySensorConfigForm(ColonelComponentForm):
|
|
198
|
-
pin =
|
|
198
|
+
pin = Select2ModelChoiceField(
|
|
199
199
|
label='Port',
|
|
200
200
|
queryset=ColonelPin.objects.filter(input=True),
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
forward
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
]
|
|
208
|
-
)
|
|
201
|
+
url='autocomplete-colonel-pins',
|
|
202
|
+
forward=[
|
|
203
|
+
forward.Self(),
|
|
204
|
+
forward.Field('colonel'),
|
|
205
|
+
forward.Const({'input': True}, 'filters')
|
|
206
|
+
]
|
|
209
207
|
)
|
|
210
208
|
inverse = forms.TypedChoiceField(
|
|
211
209
|
choices=((1, "Yes"), (0, "No")), coerce=int, initial=1,
|
|
@@ -283,17 +281,15 @@ class ColonelBinarySensorConfigForm(ColonelComponentForm):
|
|
|
283
281
|
|
|
284
282
|
|
|
285
283
|
class ColonelButtonConfigForm(ColonelComponentForm):
|
|
286
|
-
pin =
|
|
284
|
+
pin = Select2ModelChoiceField(
|
|
287
285
|
label="Port",
|
|
288
286
|
queryset=ColonelPin.objects.filter(input=True),
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
forward
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
]
|
|
296
|
-
)
|
|
287
|
+
url='autocomplete-colonel-pins',
|
|
288
|
+
forward=[
|
|
289
|
+
forward.Self(),
|
|
290
|
+
forward.Field('colonel'),
|
|
291
|
+
forward.Const({'input': True}, 'filters')
|
|
292
|
+
]
|
|
297
293
|
)
|
|
298
294
|
action_method = forms.ChoiceField(
|
|
299
295
|
label="Action method", initial='down',
|
|
@@ -322,19 +318,17 @@ class ColonelButtonConfigForm(ColonelComponentForm):
|
|
|
322
318
|
|
|
323
319
|
|
|
324
320
|
class ColonelNumericSensorConfigForm(ColonelComponentForm, NumericSensorForm):
|
|
325
|
-
pin =
|
|
321
|
+
pin = Select2ModelChoiceField(
|
|
326
322
|
label="Port",
|
|
327
323
|
queryset=ColonelPin.objects.filter(adc=True, input=True, native=True),
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
forward
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
]
|
|
337
|
-
)
|
|
324
|
+
url='autocomplete-colonel-pins',
|
|
325
|
+
forward=[
|
|
326
|
+
forward.Self(),
|
|
327
|
+
forward.Field('colonel'),
|
|
328
|
+
forward.Const(
|
|
329
|
+
{'adc': True, 'native': True, 'input': True}, 'filters'
|
|
330
|
+
)
|
|
331
|
+
]
|
|
338
332
|
)
|
|
339
333
|
attenuation = forms.TypedChoiceField(
|
|
340
334
|
initial=0, coerce=int, choices=(
|
|
@@ -375,19 +369,17 @@ class ColonelNumericSensorConfigForm(ColonelComponentForm, NumericSensorForm):
|
|
|
375
369
|
|
|
376
370
|
|
|
377
371
|
class DS18B20SensorConfigForm(ColonelComponentForm, NumericSensorForm):
|
|
378
|
-
pin =
|
|
372
|
+
pin = Select2ModelChoiceField(
|
|
379
373
|
label="Port",
|
|
380
374
|
queryset=ColonelPin.objects.filter(input=True, native=True),
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
forward
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
]
|
|
390
|
-
)
|
|
375
|
+
url='autocomplete-colonel-pins',
|
|
376
|
+
forward=[
|
|
377
|
+
forward.Self(),
|
|
378
|
+
forward.Field('colonel'),
|
|
379
|
+
forward.Const(
|
|
380
|
+
{'native': True, 'input': True}, 'filters'
|
|
381
|
+
)
|
|
382
|
+
]
|
|
391
383
|
)
|
|
392
384
|
read_frequency_s = forms.IntegerField(
|
|
393
385
|
initial=60, min_value=1, max_value=60*60*24,
|
|
@@ -415,19 +407,17 @@ class DS18B20SensorConfigForm(ColonelComponentForm, NumericSensorForm):
|
|
|
415
407
|
|
|
416
408
|
|
|
417
409
|
class ColonelDHTSensorConfigForm(ColonelComponentForm):
|
|
418
|
-
pin =
|
|
410
|
+
pin = Select2ModelChoiceField(
|
|
419
411
|
label="Port",
|
|
420
412
|
queryset=ColonelPin.objects.filter(input=True, native=True),
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
forward
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
]
|
|
430
|
-
)
|
|
413
|
+
url='autocomplete-colonel-pins',
|
|
414
|
+
forward=[
|
|
415
|
+
forward.Self(),
|
|
416
|
+
forward.Field('colonel'),
|
|
417
|
+
forward.Const(
|
|
418
|
+
{'native': True, 'input': True}, 'filters'
|
|
419
|
+
)
|
|
420
|
+
]
|
|
431
421
|
)
|
|
432
422
|
sensor_type = forms.TypedChoiceField(
|
|
433
423
|
initial=11, coerce=int, choices=(
|
|
@@ -462,18 +452,16 @@ class ColonelDHTSensorConfigForm(ColonelComponentForm):
|
|
|
462
452
|
|
|
463
453
|
|
|
464
454
|
class BME680SensorConfigForm(ColonelComponentForm):
|
|
465
|
-
interface =
|
|
455
|
+
interface = Select2ModelChoiceField(
|
|
466
456
|
queryset=Interface.objects.filter(type='i2c'),
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
forward
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
]
|
|
476
|
-
)
|
|
457
|
+
url='autocomplete-interfaces',
|
|
458
|
+
forward=[
|
|
459
|
+
forward.Self(),
|
|
460
|
+
forward.Field('colonel'),
|
|
461
|
+
forward.Const(
|
|
462
|
+
{'type': 'i2c'}, 'filters'
|
|
463
|
+
)
|
|
464
|
+
]
|
|
477
465
|
)
|
|
478
466
|
i2c_address = forms.TypedChoiceField(
|
|
479
467
|
coerce=int, initial=118,
|
|
@@ -511,18 +499,16 @@ class BME680SensorConfigForm(ColonelComponentForm):
|
|
|
511
499
|
|
|
512
500
|
|
|
513
501
|
class MPC9808SensorConfigForm(ColonelComponentForm):
|
|
514
|
-
interface =
|
|
502
|
+
interface = Select2ModelChoiceField(
|
|
515
503
|
queryset=Interface.objects.filter(type='i2c'),
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
forward
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
]
|
|
525
|
-
)
|
|
504
|
+
url='autocomplete-interfaces',
|
|
505
|
+
forward=[
|
|
506
|
+
forward.Self(),
|
|
507
|
+
forward.Field('colonel'),
|
|
508
|
+
forward.Const(
|
|
509
|
+
{'type': 'i2c'}, 'filters'
|
|
510
|
+
)
|
|
511
|
+
]
|
|
526
512
|
)
|
|
527
513
|
i2c_address = forms.TypedChoiceField(
|
|
528
514
|
coerce=int, initial=24,
|
|
@@ -566,18 +552,16 @@ class MPC9808SensorConfigForm(ColonelComponentForm):
|
|
|
566
552
|
|
|
567
553
|
|
|
568
554
|
class ENS160SensorConfigForm(ColonelComponentForm):
|
|
569
|
-
interface =
|
|
555
|
+
interface = Select2ModelChoiceField(
|
|
570
556
|
queryset=Interface.objects.filter(type='i2c'),
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
forward
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
]
|
|
580
|
-
)
|
|
557
|
+
url='autocomplete-interfaces',
|
|
558
|
+
forward=[
|
|
559
|
+
forward.Self(),
|
|
560
|
+
forward.Field('colonel'),
|
|
561
|
+
forward.Const(
|
|
562
|
+
{'type': 'i2c'}, 'filters'
|
|
563
|
+
)
|
|
564
|
+
]
|
|
581
565
|
)
|
|
582
566
|
i2c_address = forms.TypedChoiceField(
|
|
583
567
|
coerce=int, initial=83,
|
|
@@ -616,17 +600,15 @@ class ENS160SensorConfigForm(ColonelComponentForm):
|
|
|
616
600
|
|
|
617
601
|
|
|
618
602
|
class ColonelTouchSensorConfigForm(ColonelComponentForm):
|
|
619
|
-
pin =
|
|
603
|
+
pin = Select2ModelChoiceField(
|
|
620
604
|
label="Port",
|
|
621
605
|
queryset=ColonelPin.objects.filter(input=True, capacitive=True),
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
forward
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
]
|
|
629
|
-
)
|
|
606
|
+
url='autocomplete-colonel-pins',
|
|
607
|
+
forward=[
|
|
608
|
+
forward.Self(),
|
|
609
|
+
forward.Field('colonel'),
|
|
610
|
+
forward.Const({'input': True, 'capacitive': True}, 'filters')
|
|
611
|
+
]
|
|
630
612
|
)
|
|
631
613
|
threshold = forms.IntegerField(
|
|
632
614
|
min_value=0, max_value=999999999, required=False, initial=1000,
|
|
@@ -740,17 +722,15 @@ class ColonelSwitchConfigForm(ColonelComponentForm):
|
|
|
740
722
|
|
|
741
723
|
|
|
742
724
|
class ColonelPWMOutputConfigForm(ColonelComponentForm):
|
|
743
|
-
output_pin =
|
|
725
|
+
output_pin = Select2ModelChoiceField(
|
|
744
726
|
label="Port",
|
|
745
727
|
queryset=ColonelPin.objects.filter(output=True),
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
forward
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
]
|
|
753
|
-
)
|
|
728
|
+
url='autocomplete-colonel-pins',
|
|
729
|
+
forward=[
|
|
730
|
+
forward.Self(),
|
|
731
|
+
forward.Field('colonel'),
|
|
732
|
+
forward.Const({'output': True}, 'filters')
|
|
733
|
+
]
|
|
754
734
|
)
|
|
755
735
|
min = forms.FloatField(
|
|
756
736
|
required=True, initial=0,
|
|
@@ -875,17 +855,15 @@ class ColonelPWMOutputConfigForm(ColonelComponentForm):
|
|
|
875
855
|
|
|
876
856
|
|
|
877
857
|
class ColonelRGBLightConfigForm(ColonelComponentForm):
|
|
878
|
-
output_pin =
|
|
858
|
+
output_pin = Select2ModelChoiceField(
|
|
879
859
|
label="Port",
|
|
880
860
|
queryset=ColonelPin.objects.filter(output=True, native=True),
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
forward
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
]
|
|
888
|
-
)
|
|
861
|
+
url='autocomplete-colonel-pins',
|
|
862
|
+
forward=[
|
|
863
|
+
forward.Self(),
|
|
864
|
+
forward.Field('colonel'),
|
|
865
|
+
forward.Const({'output': True, 'native': True}, 'filters')
|
|
866
|
+
]
|
|
889
867
|
)
|
|
890
868
|
num_leds = forms.IntegerField(
|
|
891
869
|
label=_("Number of leds"), min_value=1, max_value=2000
|
|
@@ -998,17 +976,15 @@ class ColonelRGBLightConfigForm(ColonelComponentForm):
|
|
|
998
976
|
|
|
999
977
|
|
|
1000
978
|
class DualMotorValveForm(ColonelComponentForm):
|
|
1001
|
-
open_pin =
|
|
979
|
+
open_pin = Select2ModelChoiceField(
|
|
1002
980
|
label="Open Relay Port",
|
|
1003
981
|
queryset=ColonelPin.objects.filter(output=True),
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
forward
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
]
|
|
1011
|
-
)
|
|
982
|
+
url='autocomplete-colonel-pins',
|
|
983
|
+
forward=[
|
|
984
|
+
forward.Self(),
|
|
985
|
+
forward.Field('colonel'),
|
|
986
|
+
forward.Const({'output': True}, 'filters')
|
|
987
|
+
]
|
|
1012
988
|
)
|
|
1013
989
|
open_action = forms.ChoiceField(
|
|
1014
990
|
choices=(('HIGH', "HIGH"), ('LOW', "LOW")),
|
|
@@ -1017,17 +993,15 @@ class DualMotorValveForm(ColonelComponentForm):
|
|
|
1017
993
|
required=True, min_value=0.01, max_value=1000000000,
|
|
1018
994
|
initial=2, help_text="Time in seconds to open."
|
|
1019
995
|
)
|
|
1020
|
-
close_pin =
|
|
996
|
+
close_pin = Select2ModelChoiceField(
|
|
1021
997
|
label="Close Relay Port",
|
|
1022
998
|
queryset=ColonelPin.objects.filter(output=True),
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
forward
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
]
|
|
1030
|
-
)
|
|
999
|
+
url='autocomplete-colonel-pins',
|
|
1000
|
+
forward=[
|
|
1001
|
+
forward.Self(),
|
|
1002
|
+
forward.Field('colonel'),
|
|
1003
|
+
forward.Const({'output': True}, 'filters')
|
|
1004
|
+
]
|
|
1031
1005
|
)
|
|
1032
1006
|
close_action = forms.ChoiceField(
|
|
1033
1007
|
choices=(('HIGH', "HIGH"), ('LOW', "LOW")),
|
|
@@ -1064,32 +1038,28 @@ class DualMotorValveForm(ColonelComponentForm):
|
|
|
1064
1038
|
|
|
1065
1039
|
|
|
1066
1040
|
class BlindsConfigForm(ColonelComponentForm):
|
|
1067
|
-
open_pin =
|
|
1041
|
+
open_pin = Select2ModelChoiceField(
|
|
1068
1042
|
label="Open Relay Port",
|
|
1069
1043
|
queryset=ColonelPin.objects.filter(output=True),
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
forward
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
]
|
|
1077
|
-
)
|
|
1044
|
+
url='autocomplete-colonel-pins',
|
|
1045
|
+
forward=[
|
|
1046
|
+
forward.Self(),
|
|
1047
|
+
forward.Field('colonel'),
|
|
1048
|
+
forward.Const({'output': True}, 'filters')
|
|
1049
|
+
]
|
|
1078
1050
|
)
|
|
1079
1051
|
open_action = forms.ChoiceField(
|
|
1080
1052
|
choices=(('HIGH', "HIGH"), ('LOW', "LOW")),
|
|
1081
1053
|
)
|
|
1082
|
-
close_pin =
|
|
1054
|
+
close_pin = Select2ModelChoiceField(
|
|
1083
1055
|
label="Close Relay Port",
|
|
1084
1056
|
queryset=ColonelPin.objects.filter(output=True),
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
forward
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
]
|
|
1092
|
-
)
|
|
1057
|
+
url='autocomplete-colonel-pins',
|
|
1058
|
+
forward=[
|
|
1059
|
+
forward.Self(),
|
|
1060
|
+
forward.Field('colonel'),
|
|
1061
|
+
forward.Const({'output': True}, 'filters')
|
|
1062
|
+
]
|
|
1093
1063
|
)
|
|
1094
1064
|
close_action = forms.ChoiceField(
|
|
1095
1065
|
choices=(('HIGH', "HIGH"), ('LOW', "LOW")),
|
|
@@ -1212,29 +1182,25 @@ class BlindsConfigForm(ColonelComponentForm):
|
|
|
1212
1182
|
|
|
1213
1183
|
|
|
1214
1184
|
class BurglarSmokeDetectorConfigForm(ColonelComponentForm):
|
|
1215
|
-
power_pin =
|
|
1185
|
+
power_pin = Select2ModelChoiceField(
|
|
1216
1186
|
label="Power port",
|
|
1217
1187
|
queryset=ColonelPin.objects.filter(output=True),
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
forward
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
]
|
|
1225
|
-
)
|
|
1188
|
+
url='autocomplete-colonel-pins',
|
|
1189
|
+
forward=[
|
|
1190
|
+
forward.Self(),
|
|
1191
|
+
forward.Field('colonel'),
|
|
1192
|
+
forward.Const({'output': True}, 'filters')
|
|
1193
|
+
]
|
|
1226
1194
|
)
|
|
1227
|
-
sensor_pin =
|
|
1195
|
+
sensor_pin = Select2ModelChoiceField(
|
|
1228
1196
|
label="Sensor port",
|
|
1229
1197
|
queryset=ColonelPin.objects.filter(input=True),
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
forward
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
]
|
|
1237
|
-
)
|
|
1198
|
+
url='autocomplete-colonel-pins',
|
|
1199
|
+
forward=[
|
|
1200
|
+
forward.Self(),
|
|
1201
|
+
forward.Field('colonel'),
|
|
1202
|
+
forward.Const({'input': True}, 'filters')
|
|
1203
|
+
]
|
|
1238
1204
|
)
|
|
1239
1205
|
sensor_inverse = forms.TypedChoiceField(
|
|
1240
1206
|
choices=((0, "No"), (1, "Yes")), coerce=int, initial=0,
|
|
@@ -1306,18 +1272,16 @@ class TTLockConfigForm(ColonelComponentForm):
|
|
|
1306
1272
|
|
|
1307
1273
|
|
|
1308
1274
|
class DALIDeviceConfigForm(ColonelComponentForm):
|
|
1309
|
-
interface =
|
|
1275
|
+
interface = Select2ModelChoiceField(
|
|
1310
1276
|
queryset=Interface.objects.filter(type='dali'),
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
forward
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
]
|
|
1320
|
-
)
|
|
1277
|
+
url='autocomplete-interfaces',
|
|
1278
|
+
forward=[
|
|
1279
|
+
forward.Self(),
|
|
1280
|
+
forward.Field('colonel'),
|
|
1281
|
+
forward.Const(
|
|
1282
|
+
{'type': 'dali'}, 'filters'
|
|
1283
|
+
)
|
|
1284
|
+
]
|
|
1321
1285
|
)
|
|
1322
1286
|
|
|
1323
1287
|
def clean_interface(self):
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Generated by Django 4.2.10 on 2024-10-09 09:16
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
('fleet', '0037_alter_colonelpin_options_alter_colonelpin_no_and_more'),
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
operations = [
|
|
13
|
+
migrations.AlterField(
|
|
14
|
+
model_name='colonel',
|
|
15
|
+
name='type',
|
|
16
|
+
field=models.CharField(choices=[('4-relays', '4 Relay'), ('ample-wall', 'Ample Wall'), ('game-changer', 'Game Changer'), ('game-changer-mini', 'Game Changer Mini')], default='ample-wall', max_length=20),
|
|
17
|
+
),
|
|
18
|
+
]
|
|
Binary file
|
|
Binary file
|
simo/settings.py
CHANGED
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Generated by Django 4.2.10 on 2024-10-09 09:16
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
('users', '0032_remove_userdevice_user_alter_userdevice_users'),
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
operations = [
|
|
13
|
+
migrations.AlterField(
|
|
14
|
+
model_name='user',
|
|
15
|
+
name='ssh_key',
|
|
16
|
+
field=models.TextField(blank=True, help_text='Will be placed in /root/.ssh/authorized_keys if user is active and is master of a hub.', null=True),
|
|
17
|
+
),
|
|
18
|
+
]
|
|
Binary file
|
simo/users/models.py
CHANGED
|
@@ -252,10 +252,15 @@ class User(AbstractBaseUser, SimoAdminMixin):
|
|
|
252
252
|
'''Used by API serializer to get users role on a given instance.'''
|
|
253
253
|
if not self._instance:
|
|
254
254
|
return None
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
255
|
+
cache_key = f'user-{self.id}_instance-{self._instance.id}-role-id'
|
|
256
|
+
cached_val = cache.get(cache_key, 'expired')
|
|
257
|
+
if cached_val == 'expired':
|
|
258
|
+
for role in self.roles.all().select_related('instance'):
|
|
259
|
+
if role.instance == self._instance:
|
|
260
|
+
cached_val = role.id
|
|
261
|
+
cache.set(cache_key, role.id, 20)
|
|
262
|
+
return cached_val
|
|
263
|
+
return cached_val
|
|
259
264
|
|
|
260
265
|
@role_id.setter
|
|
261
266
|
def role_id(self, id):
|
|
@@ -290,7 +295,6 @@ class User(AbstractBaseUser, SimoAdminMixin):
|
|
|
290
295
|
)
|
|
291
296
|
])
|
|
292
297
|
cache.set(cache_key, instances, 10)
|
|
293
|
-
print("INSTANCES: ", instances)
|
|
294
298
|
return instances
|
|
295
299
|
|
|
296
300
|
@property
|
|
@@ -301,26 +305,44 @@ class User(AbstractBaseUser, SimoAdminMixin):
|
|
|
301
305
|
|
|
302
306
|
@property
|
|
303
307
|
def is_active(self):
|
|
304
|
-
if
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
308
|
+
if not self._instance:
|
|
309
|
+
cache_key = f'user-{self.id}_is_active'
|
|
310
|
+
else:
|
|
311
|
+
cache_key = f'user-{self.id}_is_active_instance-{self._instance.id}'
|
|
312
|
+
cached_value = cache.get(cache_key, 'expired')
|
|
313
|
+
if cached_value == 'expired':
|
|
314
|
+
if self.is_master and not self.instance_roles.all():
|
|
315
|
+
# Master who have no roles on any instance are in GOD mode!
|
|
316
|
+
# It can not be disabled by anybody, nor it is seen by anybody. :)
|
|
317
|
+
cached_value = True
|
|
318
|
+
elif self._instance:
|
|
319
|
+
cached_value = bool(
|
|
320
|
+
self.instance_roles.filter(
|
|
321
|
+
instance=self._instance, is_active=True
|
|
322
|
+
).first()
|
|
323
|
+
)
|
|
324
|
+
else:
|
|
325
|
+
cached_value = any(
|
|
326
|
+
[ir.is_active for ir in self.instance_roles.all()]
|
|
327
|
+
)
|
|
328
|
+
cache.set(cache_key, cached_value, 20)
|
|
329
|
+
return cached_value
|
|
315
330
|
|
|
316
331
|
|
|
317
332
|
@is_active.setter
|
|
318
333
|
def is_active(self, val):
|
|
319
334
|
if not self._instance:
|
|
320
335
|
return
|
|
336
|
+
|
|
321
337
|
self.instance_roles.filter(
|
|
322
338
|
instance=self._instance
|
|
323
339
|
).update(is_active=bool(val))
|
|
340
|
+
cache_key = f'user-{self.id}_is_active_instance-{self._instance.id}'
|
|
341
|
+
try:
|
|
342
|
+
cache.delete(cache_key)
|
|
343
|
+
except:
|
|
344
|
+
pass
|
|
345
|
+
|
|
324
346
|
rebuild_authorized_keys()
|
|
325
347
|
|
|
326
348
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: simo
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.4.2
|
|
4
4
|
Summary: Smart Home on Steroids!
|
|
5
5
|
Author-email: Simanas Venčkauskas <simanas@simo.io>
|
|
6
6
|
Project-URL: Homepage, https://simo.io
|
|
@@ -49,4 +49,5 @@ Requires-Dist: django-markdownify ==0.9.5
|
|
|
49
49
|
Requires-Dist: django-activity-stream ==2.0.0
|
|
50
50
|
Requires-Dist: gunicorn ==23.0.0
|
|
51
51
|
Requires-Dist: python-crontab ==3.2.0
|
|
52
|
+
Requires-Dist: django-object-actions ==4.3.0
|
|
52
53
|
|