simo 2.0.24__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.

Files changed (37) hide show
  1. simo/core/__pycache__/admin.cpython-38.pyc +0 -0
  2. simo/core/__pycache__/api.cpython-38.pyc +0 -0
  3. simo/core/__pycache__/controllers.cpython-38.pyc +0 -0
  4. simo/core/__pycache__/forms.cpython-38.pyc +0 -0
  5. simo/core/__pycache__/middleware.cpython-38.pyc +0 -0
  6. simo/core/__pycache__/models.cpython-38.pyc +0 -0
  7. simo/core/__pycache__/permissions.cpython-38.pyc +0 -0
  8. simo/core/__pycache__/serializers.cpython-38.pyc +0 -0
  9. simo/core/admin.py +1 -0
  10. simo/core/api.py +35 -20
  11. simo/core/controllers.py +5 -1
  12. simo/core/forms.py +29 -1
  13. simo/core/middleware.py +1 -0
  14. simo/core/migrations/0032_auto_20240506_0834.py +24 -0
  15. simo/core/migrations/__pycache__/0032_auto_20240506_0834.cpython-38.pyc +0 -0
  16. simo/core/models.py +4 -2
  17. simo/core/permissions.py +9 -3
  18. simo/core/serializers.py +20 -3
  19. simo/core/utils/__pycache__/json.cpython-38.pyc +0 -0
  20. simo/core/utils/json.py +20 -0
  21. simo/fleet/__pycache__/forms.cpython-38.pyc +0 -0
  22. simo/fleet/forms.py +123 -174
  23. simo/generic/__pycache__/forms.cpython-38.pyc +0 -0
  24. simo/generic/forms.py +24 -25
  25. simo/users/__pycache__/admin.cpython-38.pyc +0 -0
  26. simo/users/__pycache__/models.cpython-38.pyc +0 -0
  27. simo/users/admin.py +0 -5
  28. simo/users/migrations/0027_permissionsrole_can_manage_components.py +18 -0
  29. simo/users/migrations/0028_auto_20240506_1146.py +22 -0
  30. simo/users/migrations/__pycache__/0027_permissionsrole_can_manage_components.cpython-38.pyc +0 -0
  31. simo/users/migrations/__pycache__/0028_auto_20240506_1146.cpython-38.pyc +0 -0
  32. simo/users/models.py +7 -6
  33. {simo-2.0.24.dist-info → simo-2.0.26.dist-info}/METADATA +1 -1
  34. {simo-2.0.24.dist-info → simo-2.0.26.dist-info}/RECORD +37 -29
  35. {simo-2.0.24.dist-info → simo-2.0.26.dist-info}/LICENSE.md +0 -0
  36. {simo-2.0.24.dist-info → simo-2.0.26.dist-info}/WHEEL +0 -0
  37. {simo-2.0.24.dist-info → simo-2.0.26.dist-info}/top_level.txt +0 -0
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 != self.cleaned_data['colonel'].id:
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 self.cleaned_data['colonel']
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.get('colonel'):
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
- self.instance.config['pin_no'] = self.cleaned_data['pin'].no
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.get('colonel'):
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
- self.instance.config['pin_no'] = self.cleaned_data['pin'].no
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.get('colonel'):
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
- self.instance.config['pin_no'] = self.cleaned_data['pin'].no
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
- self.instance.config['pin_no'] = self.cleaned_data['pin'].no
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
- self.instance.config['i2c_interface'] = self.cleaned_data['interface'].no
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
- self.instance.config['i2c_interface'] = self.cleaned_data['interface'].no
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.get('colonel'):
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
- self.instance.config['pin_no'] = self.cleaned_data['pin'].no
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
- if self.instance.pk:
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._clean_controls()
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
- self.instance.config['output_pin_no'] = self.cleaned_data['output_pin'].no
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
- if self.instance.pk:
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 not self.cleaned_data.get('colonel'):
642
- return self.cleaned_data
643
- if not self.cleaned_data.get('output_pin'):
644
- return self.cleaned_data
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
- self.instance.config['output_pin_no'] = self.cleaned_data['output_pin'].no
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
- self.instance.config['output_pin_no'] = self.cleaned_data['output_pin'].no
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
- if not self.cleaned_data.get('controls'):
765
- return self.cleaned_data
766
-
767
- self._clean_controls()
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
 
@@ -787,7 +785,7 @@ class DualMotorValveForm(ColonelComponentForm):
787
785
  )
788
786
  open_duration = forms.FloatField(
789
787
  required=True, min_value=0.01, max_value=1000000000,
790
- help_text="Time in seconds to open."
788
+ initial=2, help_text="Time in seconds to open."
791
789
  )
792
790
  close_pin = ColonelPinChoiceField(
793
791
  queryset=ColonelPin.objects.filter(output=True),
@@ -805,27 +803,25 @@ class DualMotorValveForm(ColonelComponentForm):
805
803
  )
806
804
  close_duration = forms.FloatField(
807
805
  required=True, min_value=0.01, max_value=1000000000,
808
- help_text="Time in seconds to close."
806
+ initial=10, help_text="Time in seconds to close."
809
807
  )
810
808
 
811
809
 
812
810
  def clean(self):
813
811
  super().clean()
814
- if not self.cleaned_data.get('colonel'):
815
- return self.cleaned_data
816
- if not self.cleaned_data.get('open_pin'):
817
- return self.cleaned_data
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
- self.instance.config['open_pin_no'] = self.cleaned_data['open_pin'].no
828
- self.instance.config['close_pin_no'] = self.cleaned_data['close_pin'].no
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
- self.instance.config['open_pin_no'] = self.cleaned_data['open_pin'].no
939
- self.instance.config['close_pin_no'] = self.cleaned_data['close_pin'].no
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=((1, "Yes"), (0, "No")), coerce=int,
979
- help_text="Hint: Set pull HIGH and inverse to Yes, to get ON signal when "
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 not self.cleaned_data.get('colonel'):
987
- return self.cleaned_data
988
- if 'sensor_pin' not in self.cleaned_data:
989
- return self.cleaned_data
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
- self.instance.config['sensor_pin_no'] = self.cleaned_data['sensor_pin'].no
1029
- self.instance.config['power_pin_no'] = self.cleaned_data['power_pin'].no
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
- self.cleaned_data['colonel'].components.add(obj)
1064
- self.cleaned_data['colonel'].save()
1065
- if self.cleaned_data['door_sensor']:
1066
- GatewayObjectCommand(
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
- self.instance.config['dali_interface'] = self.cleaned_data['interface'].no
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
 
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 self.fields['is_main'].widget.attrs.get('disabled'):
318
- self.cleaned_data['is_main'] = self.fields['is_main'].initial
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['is_main'].widget.attrs.get('disabled'):
370
- self.cleaned_data['is_main'] = self.fields['is_main'].initial
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
- self.instance.config['program'] = self.controller._build_program(
545
- self.cleaned_data['contours']
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 not hasattr(component, self.cleaned_data['play_action']):
606
- self.add_error(
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
- 'reverse_action',
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
simo/users/admin.py CHANGED
@@ -16,11 +16,6 @@ class ComponentPermissionInline(admin.TabularInline):
16
16
  fields = 'component', 'read', 'write'
17
17
  readonly_fields = 'component',
18
18
 
19
- def get_queryset(self, request):
20
- return super().get_queryset(request).filter(
21
- component__show_in_app=True
22
- )
23
-
24
19
  def has_delete_permission(self, request, obj=None):
25
20
  return False
26
21
 
@@ -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
+ ]