simo 2.4.1__py3-none-any.whl → 2.5.1__py3-none-any.whl

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

Potentially problematic release.


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

Files changed (82) hide show
  1. simo/backups/__pycache__/admin.cpython-38.pyc +0 -0
  2. simo/backups/__pycache__/models.cpython-38.pyc +0 -0
  3. simo/backups/__pycache__/tasks.cpython-38.pyc +0 -0
  4. simo/backups/admin.py +10 -13
  5. simo/backups/migrations/0003_alter_backuplog_options_alter_backup_size.py +22 -0
  6. simo/backups/migrations/0004_alter_backup_options_alter_backuplog_options_and_more.py +29 -0
  7. simo/backups/migrations/__pycache__/0003_alter_backuplog_options_alter_backup_size.cpython-38.pyc +0 -0
  8. simo/backups/migrations/__pycache__/0004_alter_backup_options_alter_backuplog_options_and_more.cpython-38.pyc +0 -0
  9. simo/backups/models.py +1 -7
  10. simo/backups/tasks.py +221 -145
  11. simo/core/__pycache__/admin.cpython-38.pyc +0 -0
  12. simo/core/__pycache__/api.cpython-38.pyc +0 -0
  13. simo/core/__pycache__/app_widgets.cpython-38.pyc +0 -0
  14. simo/core/__pycache__/controllers.cpython-38.pyc +0 -0
  15. simo/core/__pycache__/events.cpython-38.pyc +0 -0
  16. simo/core/__pycache__/forms.cpython-38.pyc +0 -0
  17. simo/core/__pycache__/middleware.cpython-38.pyc +0 -0
  18. simo/core/__pycache__/models.cpython-38.pyc +0 -0
  19. simo/core/__pycache__/serializers.cpython-38.pyc +0 -0
  20. simo/core/__pycache__/tasks.cpython-38.pyc +0 -0
  21. simo/core/admin.py +4 -4
  22. simo/core/api.py +20 -4
  23. simo/core/app_widgets.py +5 -0
  24. simo/core/controllers.py +2 -2
  25. simo/core/events.py +2 -0
  26. simo/core/forms.py +2 -0
  27. simo/core/management/commands/gateways_manager.py +0 -3
  28. simo/core/middleware.py +7 -1
  29. simo/core/migrations/0042_alter_instance_timezone.py +18 -0
  30. simo/core/migrations/__pycache__/0042_alter_instance_timezone.cpython-38.pyc +0 -0
  31. simo/core/models.py +26 -6
  32. simo/core/serializers.py +17 -17
  33. simo/core/tasks.py +10 -7
  34. simo/fleet/__pycache__/controllers.cpython-38.pyc +0 -0
  35. simo/fleet/__pycache__/forms.cpython-38.pyc +0 -0
  36. simo/fleet/__pycache__/models.cpython-38.pyc +0 -0
  37. simo/fleet/controllers.py +86 -22
  38. simo/fleet/forms.py +224 -185
  39. simo/fleet/migrations/0038_alter_colonel_type.py +18 -0
  40. simo/fleet/migrations/0039_auto_20241016_1047.py +28 -0
  41. simo/fleet/migrations/0040_alter_colonel_pwm_frequency.py +18 -0
  42. simo/fleet/migrations/__pycache__/0038_alter_colonel_type.cpython-38.pyc +0 -0
  43. simo/fleet/migrations/__pycache__/0039_auto_20241016_1047.cpython-38.pyc +0 -0
  44. simo/fleet/migrations/__pycache__/0040_alter_colonel_pwm_frequency.cpython-38.pyc +0 -0
  45. simo/fleet/models.py +2 -2
  46. simo/generic/__pycache__/controllers.cpython-38.pyc +0 -0
  47. simo/generic/__pycache__/forms.cpython-38.pyc +0 -0
  48. simo/generic/__pycache__/gateways.cpython-38.pyc +0 -0
  49. simo/generic/__pycache__/models.cpython-38.pyc +0 -0
  50. simo/generic/controllers.py +41 -2
  51. simo/generic/forms.py +71 -7
  52. simo/generic/models.py +0 -1
  53. simo/generic/scripting/__init__.py +16 -0
  54. simo/generic/scripting/__pycache__/__init__.cpython-38.pyc +0 -0
  55. simo/generic/scripting/__pycache__/serializers.cpython-38.pyc +0 -0
  56. simo/generic/scripting/helpers.py +35 -0
  57. simo/generic/scripting/serializers.py +77 -0
  58. simo/generic/templates/admin/controller_widgets/weather_forecast.html +2 -2
  59. simo/notifications/__pycache__/models.cpython-38.pyc +0 -0
  60. simo/notifications/__pycache__/utils.cpython-38.pyc +0 -0
  61. simo/notifications/utils.py +30 -12
  62. simo/scripting.py +2 -2
  63. simo/users/__pycache__/api.cpython-38.pyc +0 -0
  64. simo/users/__pycache__/managers.cpython-38.pyc +0 -0
  65. simo/users/__pycache__/models.cpython-38.pyc +0 -0
  66. simo/users/__pycache__/serializers.cpython-38.pyc +0 -0
  67. simo/users/__pycache__/utils.cpython-38.pyc +0 -0
  68. simo/users/api.py +36 -7
  69. simo/users/managers.py +5 -1
  70. simo/users/migrations/0033_alter_user_ssh_key.py +18 -0
  71. simo/users/migrations/0034_instanceuser_last_seen_location_and_more.py +24 -0
  72. simo/users/migrations/__pycache__/0033_alter_user_ssh_key.cpython-38.pyc +0 -0
  73. simo/users/migrations/__pycache__/0034_instanceuser_last_seen_location_and_more.cpython-38.pyc +0 -0
  74. simo/users/models.py +37 -32
  75. simo/users/serializers.py +11 -8
  76. simo/users/utils.py +14 -3
  77. {simo-2.4.1.dist-info → simo-2.5.1.dist-info}/METADATA +1 -1
  78. {simo-2.4.1.dist-info → simo-2.5.1.dist-info}/RECORD +82 -59
  79. {simo-2.4.1.dist-info → simo-2.5.1.dist-info}/WHEEL +1 -1
  80. {simo-2.4.1.dist-info → simo-2.5.1.dist-info}/LICENSE.md +0 -0
  81. {simo-2.4.1.dist-info → simo-2.5.1.dist-info}/entry_points.txt +0 -0
  82. {simo-2.4.1.dist-info → simo-2.5.1.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 = ColonelPinChoiceField(
198
+ pin = Select2ModelChoiceField(
199
199
  label='Port',
200
200
  queryset=ColonelPin.objects.filter(input=True),
201
- widget=autocomplete.ListSelect2(
202
- url='autocomplete-colonel-pins',
203
- forward=[
204
- forward.Self(),
205
- forward.Field('colonel'),
206
- forward.Const({'input': True}, 'filters')
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 = ColonelPinChoiceField(
284
+ pin = Select2ModelChoiceField(
287
285
  label="Port",
288
286
  queryset=ColonelPin.objects.filter(input=True),
289
- widget=autocomplete.ListSelect2(
290
- url='autocomplete-colonel-pins',
291
- forward=[
292
- forward.Self(),
293
- forward.Field('colonel'),
294
- forward.Const({'input': True}, 'filters')
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 = ColonelPinChoiceField(
321
+ pin = Select2ModelChoiceField(
326
322
  label="Port",
327
323
  queryset=ColonelPin.objects.filter(adc=True, input=True, native=True),
328
- widget=autocomplete.ListSelect2(
329
- url='autocomplete-colonel-pins',
330
- forward=[
331
- forward.Self(),
332
- forward.Field('colonel'),
333
- forward.Const(
334
- {'adc': True, 'native': True, 'input': True}, 'filters'
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 = ColonelPinChoiceField(
372
+ pin = Select2ModelChoiceField(
379
373
  label="Port",
380
374
  queryset=ColonelPin.objects.filter(input=True, native=True),
381
- widget=autocomplete.ListSelect2(
382
- url='autocomplete-colonel-pins',
383
- forward=[
384
- forward.Self(),
385
- forward.Field('colonel'),
386
- forward.Const(
387
- {'native': True, 'input': True}, 'filters'
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 = ColonelPinChoiceField(
410
+ pin = Select2ModelChoiceField(
419
411
  label="Port",
420
412
  queryset=ColonelPin.objects.filter(input=True, native=True),
421
- widget=autocomplete.ListSelect2(
422
- url='autocomplete-colonel-pins',
423
- forward=[
424
- forward.Self(),
425
- forward.Field('colonel'),
426
- forward.Const(
427
- {'native': True, 'input': True}, 'filters'
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 = ColonelInterfacesChoiceField(
455
+ interface = Select2ModelChoiceField(
466
456
  queryset=Interface.objects.filter(type='i2c'),
467
- widget=autocomplete.ListSelect2(
468
- url='autocomplete-interfaces',
469
- forward=[
470
- forward.Self(),
471
- forward.Field('colonel'),
472
- forward.Const(
473
- {'type': 'i2c'}, 'filters'
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 = ColonelInterfacesChoiceField(
502
+ interface = Select2ModelChoiceField(
515
503
  queryset=Interface.objects.filter(type='i2c'),
516
- widget=autocomplete.ListSelect2(
517
- url='autocomplete-interfaces',
518
- forward=[
519
- forward.Self(),
520
- forward.Field('colonel'),
521
- forward.Const(
522
- {'type': 'i2c'}, 'filters'
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 = ColonelInterfacesChoiceField(
555
+ interface = Select2ModelChoiceField(
570
556
  queryset=Interface.objects.filter(type='i2c'),
571
- widget=autocomplete.ListSelect2(
572
- url='autocomplete-interfaces',
573
- forward=[
574
- forward.Self(),
575
- forward.Field('colonel'),
576
- forward.Const(
577
- {'type': 'i2c'}, 'filters'
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 = ColonelPinChoiceField(
603
+ pin = Select2ModelChoiceField(
620
604
  label="Port",
621
605
  queryset=ColonelPin.objects.filter(input=True, capacitive=True),
622
- widget=autocomplete.ListSelect2(
623
- url='autocomplete-colonel-pins',
624
- forward=[
625
- forward.Self(),
626
- forward.Field('colonel'),
627
- forward.Const({'input': True, 'capacitive': True}, 'filters')
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 = ColonelPinChoiceField(
725
+ output_pin = Select2ModelChoiceField(
744
726
  label="Port",
745
727
  queryset=ColonelPin.objects.filter(output=True),
746
- widget=autocomplete.ListSelect2(
747
- url='autocomplete-colonel-pins',
748
- forward=[
749
- forward.Self(),
750
- forward.Field('colonel'),
751
- forward.Const({'output': True}, 'filters')
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,
@@ -761,14 +741,21 @@ class ColonelPWMOutputConfigForm(ColonelComponentForm):
761
741
  help_text="Maximum component value"
762
742
  )
763
743
  value_units = forms.CharField(required=False)
764
- duty_min = forms.IntegerField(
765
- min_value=0, max_value=1023, required=True, initial=0,
766
- help_text="Minumum PWM signal output duty (0 - 1023)"
744
+
745
+ device_min = forms.IntegerField(
746
+ label="Device minimum (%).",
747
+ help_text="Device will turn off once it reaches this internal value. "
748
+ "Usually it is a good idea to "
749
+ "set this somewhere in between of 5 - 15 %. ",
750
+ initial=10, min_value=0, max_value=100,
767
751
  )
768
- duty_max = forms.IntegerField(
769
- min_value=0, max_value=1023, required=True, initial=900,
770
- help_text="Maximum PWM signal output duty (0 - 1023)"
752
+ device_max = forms.IntegerField(
753
+ label="Device maximum (%).",
754
+ help_text="Can be used to prevent reaching maximum values. "
755
+ "Default is 100%",
756
+ initial=100, min_value=0, max_value=100,
771
757
  )
758
+
772
759
  turn_on_time = forms.IntegerField(
773
760
  min_value=0, max_value=60000, initial=1000,
774
761
  help_text="Turn on speed in ms. 1500 is a great quick default. "
@@ -783,9 +770,6 @@ class ColonelPWMOutputConfigForm(ColonelComponentForm):
783
770
  initial='easeOutSine', choices=EASING_CHOICES,
784
771
  help_text="easeOutSine - offers most naturally looking effect."
785
772
  )
786
- inverse = forms.BooleanField(
787
- label=_("Inverse dimmer signal"), required=False, initial=True
788
- )
789
773
  on_value = forms.FloatField(
790
774
  required=True, initial=100,
791
775
  help_text="ON value when used with toggle switch"
@@ -874,18 +858,81 @@ class ColonelPWMOutputConfigForm(ColonelComponentForm):
874
858
  return obj
875
859
 
876
860
 
861
+ class DCDriverConfigForm(ColonelComponentForm):
862
+ output_pin = Select2ModelChoiceField(
863
+ label="Port",
864
+ queryset=ColonelPin.objects.filter(output=True),
865
+ url='autocomplete-colonel-pins',
866
+ forward=[
867
+ forward.Self(),
868
+ forward.Field('colonel'),
869
+ forward.Const({'output': True}, 'filters')
870
+ ]
871
+ )
872
+ min = forms.FloatField(
873
+ required=True, initial=0,
874
+ help_text="Minimum component value displayed to the user."
875
+ )
876
+ max = forms.FloatField(
877
+ required=True, initial=24,
878
+ help_text="Maximum component value displayed to the user."
879
+ )
880
+ value_units = forms.CharField(required=False)
881
+
882
+ device_min = forms.FloatField(
883
+ label="Device minimum Voltage.",
884
+ help_text="This will be the lowest possible voltage value of a device.\n"
885
+ "Don't forget to adjust your component min value accordingly "
886
+ "if you change this.",
887
+ initial=0, min_value=0, max_value=24,
888
+ )
889
+ device_max = forms.IntegerField(
890
+ label="Device maximum Voltage.",
891
+ help_text="Can be set lower than it's natural maximum of 24V. \n"
892
+ "Don't forget to adjust your component max value accordingly "
893
+ "if you change this.",
894
+ initial=24, min_value=0, max_value=24,
895
+ )
896
+
897
+ def clean(self):
898
+ super().clean()
899
+ if 'output_pin' in self.cleaned_data:
900
+ self._clean_pin('output_pin')
901
+ return self.cleaned_data
902
+
903
+
904
+ def save(self, commit=True):
905
+ if 'output_pin' in self.cleaned_data:
906
+ self.instance.config['output_pin_no'] = self.cleaned_data['output_pin'].no
907
+
908
+ update_colonel = False
909
+ if not self.instance.pk:
910
+ update_colonel = True
911
+ elif 'output_pin' in self.changed_data:
912
+ update_colonel = True
913
+
914
+ obj = super().save(commit=commit)
915
+
916
+ if not update_colonel:
917
+ GatewayObjectCommand(
918
+ obj.gateway, self.cleaned_data['colonel'], id=obj.id,
919
+ command='call', method='update_config', args=[
920
+ obj.controller._get_colonel_config()
921
+ ]
922
+ ).publish()
923
+ return obj
924
+
925
+
877
926
  class ColonelRGBLightConfigForm(ColonelComponentForm):
878
- output_pin = ColonelPinChoiceField(
927
+ output_pin = Select2ModelChoiceField(
879
928
  label="Port",
880
929
  queryset=ColonelPin.objects.filter(output=True, native=True),
881
- widget=autocomplete.ListSelect2(
882
- url='autocomplete-colonel-pins',
883
- forward=[
884
- forward.Self(),
885
- forward.Field('colonel'),
886
- forward.Const({'output': True, 'native': True}, 'filters')
887
- ]
888
- )
930
+ url='autocomplete-colonel-pins',
931
+ forward=[
932
+ forward.Self(),
933
+ forward.Field('colonel'),
934
+ forward.Const({'output': True, 'native': True}, 'filters')
935
+ ]
889
936
  )
890
937
  num_leds = forms.IntegerField(
891
938
  label=_("Number of leds"), min_value=1, max_value=2000
@@ -998,17 +1045,15 @@ class ColonelRGBLightConfigForm(ColonelComponentForm):
998
1045
 
999
1046
 
1000
1047
  class DualMotorValveForm(ColonelComponentForm):
1001
- open_pin = ColonelPinChoiceField(
1048
+ open_pin = Select2ModelChoiceField(
1002
1049
  label="Open Relay Port",
1003
1050
  queryset=ColonelPin.objects.filter(output=True),
1004
- widget=autocomplete.ListSelect2(
1005
- url='autocomplete-colonel-pins',
1006
- forward=[
1007
- forward.Self(),
1008
- forward.Field('colonel'),
1009
- forward.Const({'output': True}, 'filters')
1010
- ]
1011
- )
1051
+ url='autocomplete-colonel-pins',
1052
+ forward=[
1053
+ forward.Self(),
1054
+ forward.Field('colonel'),
1055
+ forward.Const({'output': True}, 'filters')
1056
+ ]
1012
1057
  )
1013
1058
  open_action = forms.ChoiceField(
1014
1059
  choices=(('HIGH', "HIGH"), ('LOW', "LOW")),
@@ -1017,17 +1062,15 @@ class DualMotorValveForm(ColonelComponentForm):
1017
1062
  required=True, min_value=0.01, max_value=1000000000,
1018
1063
  initial=2, help_text="Time in seconds to open."
1019
1064
  )
1020
- close_pin = ColonelPinChoiceField(
1065
+ close_pin = Select2ModelChoiceField(
1021
1066
  label="Close Relay Port",
1022
1067
  queryset=ColonelPin.objects.filter(output=True),
1023
- widget=autocomplete.ListSelect2(
1024
- url='autocomplete-colonel-pins',
1025
- forward=[
1026
- forward.Self(),
1027
- forward.Field('colonel'),
1028
- forward.Const({'output': True}, 'filters')
1029
- ]
1030
- )
1068
+ url='autocomplete-colonel-pins',
1069
+ forward=[
1070
+ forward.Self(),
1071
+ forward.Field('colonel'),
1072
+ forward.Const({'output': True}, 'filters')
1073
+ ]
1031
1074
  )
1032
1075
  close_action = forms.ChoiceField(
1033
1076
  choices=(('HIGH', "HIGH"), ('LOW', "LOW")),
@@ -1036,6 +1079,12 @@ class DualMotorValveForm(ColonelComponentForm):
1036
1079
  required=True, min_value=0.01, max_value=1000000000,
1037
1080
  initial=10, help_text="Time in seconds to close."
1038
1081
  )
1082
+ min = forms.FloatField(
1083
+ label="Minimum displayed value", required=True, initial=0
1084
+ )
1085
+ max = forms.FloatField(
1086
+ label="Maximum displayed value", required=True, initial=100
1087
+ )
1039
1088
 
1040
1089
 
1041
1090
  def clean(self):
@@ -1064,32 +1113,28 @@ class DualMotorValveForm(ColonelComponentForm):
1064
1113
 
1065
1114
 
1066
1115
  class BlindsConfigForm(ColonelComponentForm):
1067
- open_pin = ColonelPinChoiceField(
1116
+ open_pin = Select2ModelChoiceField(
1068
1117
  label="Open Relay Port",
1069
1118
  queryset=ColonelPin.objects.filter(output=True),
1070
- widget=autocomplete.ListSelect2(
1071
- url='autocomplete-colonel-pins',
1072
- forward=[
1073
- forward.Self(),
1074
- forward.Field('colonel'),
1075
- forward.Const({'output': True}, 'filters')
1076
- ]
1077
- )
1119
+ url='autocomplete-colonel-pins',
1120
+ forward=[
1121
+ forward.Self(),
1122
+ forward.Field('colonel'),
1123
+ forward.Const({'output': True}, 'filters')
1124
+ ]
1078
1125
  )
1079
1126
  open_action = forms.ChoiceField(
1080
1127
  choices=(('HIGH', "HIGH"), ('LOW', "LOW")),
1081
1128
  )
1082
- close_pin = ColonelPinChoiceField(
1129
+ close_pin = Select2ModelChoiceField(
1083
1130
  label="Close Relay Port",
1084
1131
  queryset=ColonelPin.objects.filter(output=True),
1085
- widget=autocomplete.ListSelect2(
1086
- url='autocomplete-colonel-pins',
1087
- forward=[
1088
- forward.Self(),
1089
- forward.Field('colonel'),
1090
- forward.Const({'output': True}, 'filters')
1091
- ]
1092
- )
1132
+ url='autocomplete-colonel-pins',
1133
+ forward=[
1134
+ forward.Self(),
1135
+ forward.Field('colonel'),
1136
+ forward.Const({'output': True}, 'filters')
1137
+ ]
1093
1138
  )
1094
1139
  close_action = forms.ChoiceField(
1095
1140
  choices=(('HIGH', "HIGH"), ('LOW', "LOW")),
@@ -1212,29 +1257,25 @@ class BlindsConfigForm(ColonelComponentForm):
1212
1257
 
1213
1258
 
1214
1259
  class BurglarSmokeDetectorConfigForm(ColonelComponentForm):
1215
- power_pin = ColonelPinChoiceField(
1260
+ power_pin = Select2ModelChoiceField(
1216
1261
  label="Power port",
1217
1262
  queryset=ColonelPin.objects.filter(output=True),
1218
- widget=autocomplete.ListSelect2(
1219
- url='autocomplete-colonel-pins',
1220
- forward=[
1221
- forward.Self(),
1222
- forward.Field('colonel'),
1223
- forward.Const({'output': True}, 'filters')
1224
- ]
1225
- )
1263
+ url='autocomplete-colonel-pins',
1264
+ forward=[
1265
+ forward.Self(),
1266
+ forward.Field('colonel'),
1267
+ forward.Const({'output': True}, 'filters')
1268
+ ]
1226
1269
  )
1227
- sensor_pin = ColonelPinChoiceField(
1270
+ sensor_pin = Select2ModelChoiceField(
1228
1271
  label="Sensor port",
1229
1272
  queryset=ColonelPin.objects.filter(input=True),
1230
- widget=autocomplete.ListSelect2(
1231
- url='autocomplete-colonel-pins',
1232
- forward=[
1233
- forward.Self(),
1234
- forward.Field('colonel'),
1235
- forward.Const({'input': True}, 'filters')
1236
- ]
1237
- )
1273
+ url='autocomplete-colonel-pins',
1274
+ forward=[
1275
+ forward.Self(),
1276
+ forward.Field('colonel'),
1277
+ forward.Const({'input': True}, 'filters')
1278
+ ]
1238
1279
  )
1239
1280
  sensor_inverse = forms.TypedChoiceField(
1240
1281
  choices=((0, "No"), (1, "Yes")), coerce=int, initial=0,
@@ -1306,18 +1347,16 @@ class TTLockConfigForm(ColonelComponentForm):
1306
1347
 
1307
1348
 
1308
1349
  class DALIDeviceConfigForm(ColonelComponentForm):
1309
- interface = ColonelInterfacesChoiceField(
1350
+ interface = Select2ModelChoiceField(
1310
1351
  queryset=Interface.objects.filter(type='dali'),
1311
- widget=autocomplete.ListSelect2(
1312
- url='autocomplete-interfaces',
1313
- forward=[
1314
- forward.Self(),
1315
- forward.Field('colonel'),
1316
- forward.Const(
1317
- {'type': 'dali'}, 'filters'
1318
- )
1319
- ]
1320
- )
1352
+ url='autocomplete-interfaces',
1353
+ forward=[
1354
+ forward.Self(),
1355
+ forward.Field('colonel'),
1356
+ forward.Const(
1357
+ {'type': 'dali'}, 'filters'
1358
+ )
1359
+ ]
1321
1360
  )
1322
1361
 
1323
1362
  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
+ ]
@@ -0,0 +1,28 @@
1
+ # Generated by Django 4.2.10 on 2024-10-16 10:47
2
+ from django.db import migrations
3
+
4
+
5
+ def forwards_func(apps, schema_editor):
6
+ Component = apps.get_model("core", "Component")
7
+
8
+ for comp in Component.objects.filter(controller_uid='simo.fleet.controllers.PWMOutput'):
9
+ duty_min = comp.config.get('duty_min', 0)
10
+ duty_max = comp.config.get('duty_max', 1023)
11
+ comp.config['device_min'] = int((1023 - duty_max) / 1023 * 100)
12
+ comp.config['device_max'] = int(100 - (duty_min / 1023 * 100))
13
+ comp.save()
14
+
15
+
16
+ def reverse_func(apps, schema_editor):
17
+ pass
18
+
19
+
20
+ class Migration(migrations.Migration):
21
+
22
+ dependencies = [
23
+ ('fleet', '0038_alter_colonel_type'),
24
+ ]
25
+
26
+ operations = [
27
+ migrations.RunPython(forwards_func, reverse_func, elidable=True),
28
+ ]
@@ -0,0 +1,18 @@
1
+ # Generated by Django 4.2.10 on 2024-10-18 08:00
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ('fleet', '0039_auto_20241016_1047'),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AlterField(
14
+ model_name='colonel',
15
+ name='pwm_frequency',
16
+ field=models.IntegerField(choices=[(0, '3kHz'), (1, '22kHz')], default=0, help_text='Affects Ample Wall dimmer PWM output (dimmer) frequency'),
17
+ ),
18
+ ]