simo 2.1.13__py3-none-any.whl → 2.2.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.

Files changed (32) hide show
  1. simo/core/__pycache__/managers.cpython-38.pyc +0 -0
  2. simo/core/controllers.py +1 -1
  3. simo/core/managers.py +3 -3
  4. simo/fleet/__pycache__/controllers.cpython-38.pyc +0 -0
  5. simo/fleet/__pycache__/forms.cpython-38.pyc +0 -0
  6. simo/fleet/__pycache__/models.cpython-38.pyc +0 -0
  7. simo/fleet/controllers.py +36 -1
  8. simo/fleet/forms.py +76 -6
  9. simo/fleet/models.py +4 -1
  10. simo/fleet/templates/fleet/controllers_info/ENS160AirQualitySensor.md +15 -0
  11. simo/generic/__pycache__/models.cpython-38.pyc +0 -0
  12. simo/generic/controllers.py +4 -4
  13. simo/generic/models.py +12 -2
  14. simo/users/__pycache__/api.cpython-38.pyc +0 -0
  15. simo/users/__pycache__/auth_backends.cpython-38.pyc +0 -0
  16. simo/users/__pycache__/managers.cpython-38.pyc +0 -0
  17. simo/users/__pycache__/models.cpython-38.pyc +0 -0
  18. simo/users/__pycache__/serializers.cpython-38.pyc +0 -0
  19. simo/users/api.py +16 -3
  20. simo/users/auth_backends.py +5 -3
  21. simo/users/managers.py +7 -0
  22. simo/users/migrations/0001_initial.py +1 -1
  23. simo/users/migrations/__pycache__/0001_initial.cpython-38.pyc +0 -0
  24. simo/users/models.py +8 -9
  25. simo/users/serializers.py +0 -1
  26. {simo-2.1.13.dist-info → simo-2.2.2.dist-info}/METADATA +1 -1
  27. {simo-2.1.13.dist-info → simo-2.2.2.dist-info}/RECORD +32 -29
  28. {simo-2.1.13.dist-info → simo-2.2.2.dist-info}/WHEEL +1 -1
  29. /simo/fleet/templates/fleet/controllers_info/{button.md → Button.md} +0 -0
  30. {simo-2.1.13.dist-info → simo-2.2.2.dist-info}/LICENSE.md +0 -0
  31. {simo-2.1.13.dist-info → simo-2.2.2.dist-info}/entry_points.txt +0 -0
  32. {simo-2.1.13.dist-info → simo-2.2.2.dist-info}/top_level.txt +0 -0
simo/core/controllers.py CHANGED
@@ -78,7 +78,7 @@ class ControllerBase(ABC):
78
78
  @property
79
79
  def info_template_path(self) -> str:
80
80
  return f"{self.__class__.__module__.split('.')[-2]}/" \
81
- f"controllers_info/{self.__class__.__name__.lower()}.md"
81
+ f"controllers_info/{self.__class__.__name__}.md"
82
82
 
83
83
  @abstractmethod
84
84
  def _validate_val(self, value, occasion=None):
simo/core/managers.py CHANGED
@@ -25,7 +25,7 @@ class InstanceManager(models.Manager):
25
25
  class ZonesManager(models.Manager):
26
26
 
27
27
  def get_queryset(self):
28
- qs = super().get_queryset()
28
+ qs = super().get_queryset().filter(instance__is_active=True)
29
29
  instance = get_current_instance()
30
30
  if instance:
31
31
  qs = qs.filter(instance=instance)
@@ -35,7 +35,7 @@ class ZonesManager(models.Manager):
35
35
  class CategoriesManager(models.Manager):
36
36
 
37
37
  def get_queryset(self):
38
- qs = super().get_queryset()
38
+ qs = super().get_queryset().filter(instance__is_active=True)
39
39
  instance = get_current_instance()
40
40
  if instance:
41
41
  qs = qs.filter(instance=instance)
@@ -45,7 +45,7 @@ class CategoriesManager(models.Manager):
45
45
  class ComponentsManager(models.Manager):
46
46
 
47
47
  def get_queryset(self):
48
- qs = super().get_queryset()
48
+ qs = super().get_queryset().filter(zone__instance__is_active=True)
49
49
  instance = get_current_instance()
50
50
  if instance:
51
51
  qs = qs.filter(zone__instance=instance)
Binary file
simo/fleet/controllers.py CHANGED
@@ -25,7 +25,7 @@ from .forms import (
25
25
  ColonelSwitchConfigForm, ColonelPWMOutputConfigForm,
26
26
  ColonelNumericSensorConfigForm, ColonelRGBLightConfigForm,
27
27
  ColonelDHTSensorConfigForm, DS18B20SensorConfigForm,
28
- BME680SensorConfigForm, MPC9808SensorConfigForm,
28
+ BME680SensorConfigForm, MPC9808SensorConfigForm, ENS160SensorConfigForm,
29
29
  DualMotorValveForm, BlindsConfigForm, BurglarSmokeDetectorConfigForm,
30
30
  TTLockConfigForm, DALIDeviceConfigForm, DaliLampForm, DaliGearGroupForm,
31
31
  DaliSwitchConfigForm,
@@ -210,6 +210,41 @@ class MPC9808TempSensor(FleeDeviceMixin, BaseNumericSensor):
210
210
  name = "MPC9808 Temperature Sensor (I2C)"
211
211
 
212
212
 
213
+ class ENS160AirQualitySensor(FleeDeviceMixin, BaseMultiSensor):
214
+ gateway_class = FleetGatewayHandler
215
+ config_form = ENS160SensorConfigForm
216
+ name = "ENS160 Air Quality Sensor (I2C)"
217
+
218
+ default_value = [
219
+ ["CO2", 0, "ppm"],
220
+ ["TVOC", 0, "ppb"],
221
+ ["AQI (UBA)", 0, ""]
222
+ ]
223
+
224
+ def get_co2(self):
225
+ try:
226
+ for entry in self.component.value:
227
+ if entry[0] == 'CO2':
228
+ return entry[1]
229
+ except:
230
+ return
231
+
232
+ def get_tvoc(self):
233
+ try:
234
+ for entry in self.component.value:
235
+ if entry[0] == 'TVOC':
236
+ return entry[1]
237
+ except:
238
+ return
239
+
240
+ def get_aqi(self):
241
+ try:
242
+ for entry in self.component.value:
243
+ if entry[0] == 'AQI (UBA)':
244
+ return entry[1]
245
+ except:
246
+ return
247
+
213
248
 
214
249
  class BasicOutputMixin:
215
250
  gateway_class = FleetGatewayHandler
simo/fleet/forms.py CHANGED
@@ -462,9 +462,9 @@ class BME680SensorConfigForm(ColonelComponentForm):
462
462
  ]
463
463
  )
464
464
  )
465
- i2c_address = forms.IntegerField(
466
- min_value=0, max_value=127, initial=118,
467
- help_text="Integer: 0 - 127. Dafault: 118"
465
+ i2c_address = forms.TypedChoiceField(
466
+ coerce=int, initial=118,
467
+ choices=((118, "0x76"), (119, "0x77")),
468
468
  )
469
469
  read_frequency_s = forms.IntegerField(
470
470
  initial=60, min_value=1, max_value=60*60*24,
@@ -482,6 +482,13 @@ class BME680SensorConfigForm(ColonelComponentForm):
482
482
  f"This interface is on {self.cleaned_data['interface'].colonel}, "
483
483
  f"however we need an interface from {self.cleaned_data['colonel']}."
484
484
  )
485
+ other_comp = Component.objects.filter(
486
+ config__colonel=self.cleaned_data['colonel'].id,
487
+ config__interface=self.cleaned_data['interface'].id,
488
+ config__i2c_address=self.cleaned_data['i2c_address']
489
+ ).exclude(id=self.instance.id).first()
490
+ if other_comp:
491
+ self.add_error('i2c_address', f'Already occupied by {other_comp}!')
485
492
  return self.cleaned_data
486
493
 
487
494
  def save(self, commit=True):
@@ -504,9 +511,14 @@ class MPC9808SensorConfigForm(ColonelComponentForm):
504
511
  ]
505
512
  )
506
513
  )
507
- i2c_address = forms.IntegerField(
508
- min_value=0, max_value=127, initial=24,
509
- help_text="Integer: 0 - 127. Default: 24",
514
+ i2c_address = forms.TypedChoiceField(
515
+ coerce=int, initial=24,
516
+ choices=(
517
+ (24, "Default"),
518
+ (25, "AD0"), (25, "AD1"), (28, "AD2"),
519
+ (27, "AD0 + AD1"), (29, "AD0 + AD2"), (30, "AD1 + AD2"),
520
+ (31, "AD0 + AD1 + AD2")
521
+ ),
510
522
  )
511
523
  read_frequency_s = forms.IntegerField(
512
524
  initial=60, min_value=1, max_value=60 * 60 * 24,
@@ -524,6 +536,14 @@ class MPC9808SensorConfigForm(ColonelComponentForm):
524
536
  f"This interface is on {self.cleaned_data['interface'].colonel}, "
525
537
  f"however we need an interface from {self.cleaned_data['colonel']}."
526
538
  )
539
+
540
+ other_comp = Component.objects.filter(
541
+ config__colonel=self.cleaned_data['colonel'].id,
542
+ config__interface=self.cleaned_data['interface'].id,
543
+ config__i2c_address=self.cleaned_data['i2c_address']
544
+ ).exclude(id=self.instance.id).first()
545
+ if other_comp:
546
+ self.add_error('i2c_address', f'Already occupied by {other_comp}!')
527
547
  return self.cleaned_data
528
548
 
529
549
  def save(self, commit=True):
@@ -532,6 +552,56 @@ class MPC9808SensorConfigForm(ColonelComponentForm):
532
552
  return super().save(commit=commit)
533
553
 
534
554
 
555
+ class ENS160SensorConfigForm(ColonelComponentForm):
556
+ interface = ColonelInterfacesChoiceField(
557
+ queryset=Interface.objects.filter(type='i2c'),
558
+ widget=autocomplete.ListSelect2(
559
+ url='autocomplete-interfaces',
560
+ forward=[
561
+ forward.Self(),
562
+ forward.Field('colonel'),
563
+ forward.Const(
564
+ {'type': 'i2c'}, 'filters'
565
+ )
566
+ ]
567
+ )
568
+ )
569
+ i2c_address = forms.TypedChoiceField(
570
+ coerce=int, initial=83,
571
+ choices=((82, "0x52"), (83, "0x53")),
572
+ )
573
+ read_frequency_s = forms.IntegerField(
574
+ initial=10, min_value=1, max_value=60 * 60 * 24,
575
+ help_text='read and report air quality values every s. '
576
+ 'Can not be less than 1s.'
577
+
578
+ )
579
+
580
+ def clean(self):
581
+ if not self.cleaned_data.get('colonel'):
582
+ return self.cleaned_data
583
+ if self.cleaned_data['interface'].colonel != self.cleaned_data['colonel']:
584
+ self.add_error(
585
+ 'interface',
586
+ f"This interface is on {self.cleaned_data['interface'].colonel}, "
587
+ f"however we need an interface from {self.cleaned_data['colonel']}."
588
+ )
589
+ other_comp = Component.objects.filter(
590
+ config__colonel=self.cleaned_data['colonel'].id,
591
+ config__interface=self.cleaned_data['interface'].id,
592
+ config__i2c_address=self.cleaned_data['i2c_address']
593
+ ).exclude(id=self.instance.id).first()
594
+ if other_comp:
595
+ self.add_error('i2c_address', f'Already occupied by {other_comp}!')
596
+ return self.cleaned_data
597
+
598
+ def save(self, commit=True):
599
+ if 'interface' in self.cleaned_data:
600
+ self.instance.config['i2c_interface'] = \
601
+ self.cleaned_data['interface'].no
602
+ return super().save(commit=commit)
603
+
604
+
535
605
  class ColonelTouchSensorConfigForm(ColonelComponentForm):
536
606
  pin = ColonelPinChoiceField(
537
607
  label="Port",
simo/fleet/models.py CHANGED
@@ -11,7 +11,6 @@ from dirtyfields import DirtyFieldsMixin
11
11
  from simo.core.models import Instance, Gateway, Component
12
12
  from simo.core.utils.helpers import get_random_string
13
13
  from simo.core.events import GatewayObjectCommand
14
- from .gateways import FleetGatewayHandler
15
14
  from .managers import ColonelsManager, ColonelPinsManager, InterfacesManager
16
15
  from .utils import GPIO_PINS, INTERFACES_PINS_MAP
17
16
 
@@ -146,6 +145,7 @@ class Colonel(DirtyFieldsMixin, models.Model):
146
145
  return resp.json()
147
146
 
148
147
  def update_firmware(self, to_version):
148
+ from .gateways import FleetGatewayHandler
149
149
  for gateway in Gateway.objects.filter(type=FleetGatewayHandler.uid):
150
150
  GatewayObjectCommand(
151
151
  gateway, self,
@@ -153,12 +153,14 @@ class Colonel(DirtyFieldsMixin, models.Model):
153
153
  ).publish()
154
154
 
155
155
  def restart(self):
156
+ from .gateways import FleetGatewayHandler
156
157
  for gateway in Gateway.objects.filter(type=FleetGatewayHandler.uid):
157
158
  GatewayObjectCommand(
158
159
  gateway, self, command='restart'
159
160
  ).publish()
160
161
 
161
162
  def update_config(self):
163
+ from .gateways import FleetGatewayHandler
162
164
  for gateway in Gateway.objects.filter(type=FleetGatewayHandler.uid):
163
165
  GatewayObjectCommand(
164
166
  gateway, self, command='update_config'
@@ -371,6 +373,7 @@ class Interface(models.Model):
371
373
 
372
374
 
373
375
  def broadcast_reset(self):
376
+ from .gateways import FleetGatewayHandler
374
377
  gw = Gateway.objects.filter(type=FleetGatewayHandler.uid).first()
375
378
  if not gw:
376
379
  return
@@ -0,0 +1,15 @@
1
+ ### Initial Start-Up
2
+
3
+ Initial Start-Up is the time the ENS160 needs to exhibit reasonable air quality readings after
4
+ its first ever power-on.
5
+ The ENS160 sensor raw resistance signals and sensitivities will change upon first power-on.
6
+ The change in resistance is greatest in the first 48 hours of operation. Therefore, the ENS160
7
+ employs a start-up algorithm, allowing eCO2-, TVOC- and AQI-output signals to be used from
8
+ first power-on after 1 hour of operation.
9
+
10
+ ### Warm-Up
11
+
12
+ Further to “Initial Start-Up” the conditioning or “Warm-Up” period is the time required to
13
+ achieve adequate sensor stability before measuring VOCs after idle periods or power-off.
14
+ Typically, the ENS160 requires 1 minute of warm-up before reasonable air quality readings
15
+ can be expected.
@@ -1048,8 +1048,8 @@ class Watering(ControllerBase):
1048
1048
  def _perform_schedule(self):
1049
1049
  self.component.refresh_from_db()
1050
1050
  next_run = self._get_next_run()
1051
- if self.component.config['next_run'] != next_run:
1052
- self.component.config['next_run'] = next_run
1051
+ if self.component.meta['next_run'] != next_run:
1052
+ self.component.meta['next_run'] = next_run
1053
1053
  self.component.save()
1054
1054
 
1055
1055
  if self.component.value['status'] == 'running_program':
@@ -1065,8 +1065,8 @@ class Watering(ControllerBase):
1065
1065
  str(localtime.weekday() + 1)
1066
1066
  ]
1067
1067
  if not times_to_start:
1068
- if self.component.config.get('next_run'):
1069
- self.component.config['next_run'] = None
1068
+ if self.component.meta.get('next_run'):
1069
+ self.component.meta['next_run'] = None
1070
1070
  self.component.save()
1071
1071
  return
1072
1072
 
simo/generic/models.py CHANGED
@@ -6,8 +6,6 @@ from django.db.models.signals import pre_save, post_save, post_delete
6
6
  from django.dispatch import receiver
7
7
  from simo.core.models import Instance, Component
8
8
  from simo.users.models import InstanceUser
9
- from .controllers import AlarmGroup
10
-
11
9
 
12
10
 
13
11
  @receiver(post_save, sender=Component)
@@ -20,6 +18,8 @@ def handle_alarm_groups(sender, instance, *args, **kwargs):
20
18
  if 'arm_status' not in dirty_fields:
21
19
  return
22
20
 
21
+ from .controllers import AlarmGroup
22
+
23
23
  for alarm_group in Component.objects.filter(
24
24
  controller_uid=AlarmGroup.uid,
25
25
  config__components__contains=instance.id,
@@ -76,6 +76,8 @@ def handle_alarm_groups(sender, instance, *args, **kwargs):
76
76
 
77
77
  @receiver(pre_save, sender=Component)
78
78
  def manage_alarm_groups(sender, instance, *args, **kwargs):
79
+ from .controllers import AlarmGroup
80
+
79
81
  if instance.controller_uid != AlarmGroup.uid:
80
82
  return
81
83
 
@@ -104,6 +106,8 @@ def manage_alarm_groups(sender, instance, *args, **kwargs):
104
106
  def clear_alarm_group_config_on_component_delete(
105
107
  sender, instance, *args, **kwargs
106
108
  ):
109
+ from .controllers import AlarmGroup
110
+
107
111
  for ag in Component.objects.filter(
108
112
  base_type=AlarmGroup.base_type,
109
113
  config__components__contains=instance.id
@@ -120,6 +124,9 @@ def bind_controlling_locks_to_alarm_groups(sender, instance, *args, **kwargs):
120
124
  return
121
125
  if 'value' not in instance.get_dirty_fields():
122
126
  return
127
+
128
+ from .controllers import AlarmGroup
129
+
123
130
  if instance.value == 'locked':
124
131
  for ag in Component.objects.filter(
125
132
  base_type=AlarmGroup.base_type,
@@ -169,6 +176,9 @@ def bind_alarm_groups(sender, instance, created, *args, **kwargs):
169
176
  ).exclude(is_active=False).exclude(id=instance.id).count()
170
177
  if users_at_home:
171
178
  return
179
+
180
+ from .controllers import AlarmGroup
181
+
172
182
  for ag in Component.objects.filter(
173
183
  zone__instance=instance.instance,
174
184
  base_type=AlarmGroup.base_type,
Binary file
simo/users/api.py CHANGED
@@ -211,9 +211,22 @@ class InvitationsViewSet(InstanceMixin, viewsets.ModelViewSet):
211
211
  return InstanceInvitation.objects.none()
212
212
  return InstanceInvitation.objects.filter(instance=self.instance)
213
213
 
214
- def perform_create(self, serializer):
215
- serializer.save(
216
- from_user=self.request.user, instance=self.instance
214
+ def create(self, request, *args, **kwargs):
215
+ role = PermissionsRole.objects.filter(
216
+ instance=self.instance, is_default=True
217
+ ).first()
218
+ if not role:
219
+ role = PermissionsRole.objects.filter(
220
+ instance=self.instance
221
+ ).first()
222
+ if role:
223
+ request.data['role'] = role.id
224
+ serializer = self.get_serializer(data=request.data)
225
+ serializer.is_valid(raise_exception=True)
226
+ serializer.save(instance=self.instance, from_user=self.request.user)
227
+ headers = self.get_success_headers(serializer.data)
228
+ return RESTResponse(
229
+ serializer.data, status=status.HTTP_201_CREATED, headers=headers
217
230
  )
218
231
 
219
232
  @action(detail=True, methods=['post'])
@@ -1,6 +1,7 @@
1
1
  import os
2
2
  import io
3
3
  import requests
4
+ from django.db import transaction
4
5
  from django.utils import timezone
5
6
  from django.contrib.auth import get_user_model
6
7
  from django.contrib.auth.models import Permission
@@ -54,6 +55,7 @@ class SIMOUserBackend(ModelBackend):
54
55
 
55
56
  class SSOBackend(ModelBackend):
56
57
 
58
+ @transaction.atomic
57
59
  def authenticate(self, request, user_data=None, **kwargs):
58
60
  system_user_emails = ('system@simo.io', 'device@simo.io')
59
61
  if not user_data:
@@ -93,9 +95,9 @@ class SSOBackend(ModelBackend):
93
95
  if invitation:
94
96
  invitation.taken_by = user
95
97
  invitation.save()
96
- InstanceUser.objects.create(
97
- user=user, role=invitation.role,
98
- instance=invitation.instance
98
+ InstanceUser.objects.update_or_create(
99
+ user=user, instance=invitation.instance,
100
+ defaults={'role': invitation.role}
99
101
  )
100
102
 
101
103
  if user_data.get('name'):
simo/users/managers.py ADDED
@@ -0,0 +1,7 @@
1
+ from django.db import models
2
+
3
+
4
+ class ActiveInstanceManager(models.Manager):
5
+
6
+ def get_queryset(self):
7
+ return super().get_queryset().filter(instance__is_active=True)
@@ -103,7 +103,7 @@ class Migration(migrations.Migration):
103
103
  ('last_sent', models.DateTimeField(blank=True, null=True)),
104
104
  ('taken_date', models.DateTimeField(blank=True, null=True)),
105
105
  ('from_user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='issued_hub_invitations', to=settings.AUTH_USER_MODEL)),
106
- ('role', models.ForeignKey(default=simo.users.models.get_default_invitation_role, on_delete=django.db.models.deletion.CASCADE, to='users.permissionsrole')),
106
+ ('role', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='users.permissionsrole')),
107
107
  ('taken_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='accepted_hub_invitations', to=settings.AUTH_USER_MODEL)),
108
108
  ],
109
109
  options={
simo/users/models.py CHANGED
@@ -24,7 +24,7 @@ from simo.core.utils.helpers import get_random_string
24
24
  from simo.core.events import OnChangeMixin
25
25
  from .middleware import get_current_user
26
26
  from .utils import rebuild_authorized_keys
27
- from .tasks import rebuild_mqtt_acls
27
+ from .managers import ActiveInstanceManager
28
28
 
29
29
 
30
30
  class PermissionsRole(models.Model):
@@ -49,6 +49,8 @@ class PermissionsRole(models.Model):
49
49
  default=False, help_text="Default new user role."
50
50
  )
51
51
 
52
+ objects = ActiveInstanceManager()
53
+
52
54
  class Meta:
53
55
  verbose_name = "role"
54
56
  verbose_name_plural = "roles"
@@ -97,6 +99,8 @@ class InstanceUser(DirtyFieldsMixin, models.Model, OnChangeMixin):
97
99
  at_home = models.BooleanField(default=False, db_index=True)
98
100
  is_active = models.BooleanField(default=True, db_index=True)
99
101
 
102
+ objects = ActiveInstanceManager()
103
+
100
104
  class Meta:
101
105
  unique_together = 'user', 'instance'
102
106
 
@@ -529,12 +533,6 @@ def get_default_inviation_expire_date():
529
533
  return timezone.now() + datetime.timedelta(days=14)
530
534
 
531
535
 
532
- def get_default_invitation_role():
533
- role = PermissionsRole.objects.filter(is_default=True).first()
534
- if not role:
535
- return PermissionsRole.objects.all().first().id
536
- return role.id
537
-
538
536
 
539
537
  class InstanceInvitation(models.Model):
540
538
  instance = models.ForeignKey('core.Instance', on_delete=models.CASCADE)
@@ -542,8 +540,7 @@ class InstanceInvitation(models.Model):
542
540
  max_length=50, default=get_random_string, db_index=True
543
541
  )
544
542
  role = models.ForeignKey(
545
- PermissionsRole, on_delete=models.CASCADE,
546
- default=get_default_invitation_role
543
+ PermissionsRole, on_delete=models.CASCADE
547
544
  )
548
545
  issue_date = models.DateTimeField(auto_now_add=True)
549
546
  expire_date = models.DateTimeField(
@@ -561,6 +558,8 @@ class InstanceInvitation(models.Model):
561
558
  )
562
559
  taken_date = models.DateTimeField(null=True, blank=True)
563
560
 
561
+ objects = ActiveInstanceManager()
562
+
564
563
 
565
564
  class Meta:
566
565
  verbose_name = "invitation"
simo/users/serializers.py CHANGED
@@ -77,7 +77,6 @@ class InstanceInvitationSerializer(serializers.ModelSerializer):
77
77
  'instance', 'token', 'from_user', 'taken_by',
78
78
  )
79
79
 
80
-
81
80
  class FingerprintSerializer(serializers.ModelSerializer):
82
81
  type = serializers.SerializerMethodField()
83
82
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: simo
3
- Version: 2.1.13
3
+ Version: 2.2.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
@@ -23,7 +23,7 @@ simo/core/auto_urls.py,sha256=nNXEgLAAAQAhRWQDA9AbDtw-zcPKmu_pufJaSa8g818,1102
23
23
  simo/core/autocomplete_views.py,sha256=JT5LA2_Wtr60XYSAIqaXFKFYPjrmkEf6yunXD9y2zco,4022
24
24
  simo/core/base_types.py,sha256=qVh6MrXZEfN7bFOyFftC7u0yyz0PkvpsjllLBc6SCp4,616
25
25
  simo/core/context.py,sha256=98PXAMie43faRVBFkOG22uNpvGRNprcGhzjBFkrxaRY,1367
26
- simo/core/controllers.py,sha256=2XqqnpZy1nYwowflIKI4lcNMbotERf_YkbY0ztExfbY,29529
26
+ simo/core/controllers.py,sha256=y6nB_iv1KxsyNutGRYZIBAHua2BHuV7OMRBD9nJXFbg,29521
27
27
  simo/core/dynamic_settings.py,sha256=U9pY7p_hoeD1LxobIvxZqQ7Zn_4MhYMqZvsr4O0PAYs,1871
28
28
  simo/core/events.py,sha256=LvtonJGNyCb6HLozs4EG0WZItnDwNdtnGQ4vTcnKvUs,4438
29
29
  simo/core/filters.py,sha256=ghtOZcrwNAkIyF5_G9Sn73NkiI71mXv0NhwCk4IyMIM,411
@@ -31,7 +31,7 @@ simo/core/form_fields.py,sha256=9tIjiEN3IE55GPyB4tOlfkd51JDne3-h8pKhpL3tLFE,2220
31
31
  simo/core/forms.py,sha256=QTPEe7nxzhg8NNCQFsVJpoO1LQW6MNNTHkEhBs7keYw,21386
32
32
  simo/core/gateways.py,sha256=m0eS3XjVe34Dge6xtoCq16kFWCKJcdQrT0JW0REqoq8,3715
33
33
  simo/core/loggers.py,sha256=EBdq23gTQScVfQVH-xeP90-wII2DQFDjoROAW6ggUP4,1645
34
- simo/core/managers.py,sha256=sCo9R7ctGepYK-uspl7cR-KFBrJxSy3TZkbWbOP0YHs,2914
34
+ simo/core/managers.py,sha256=n-b3I4uXzfHKTeB1VMjSaMsDUxp8FegFJwnbV1IsWQ4,3019
35
35
  simo/core/middleware.py,sha256=Go4DAdnXWd7i0ZEbftwfcZI5YGjZ31aQqDzWE4s676c,1970
36
36
  simo/core/models.py,sha256=Mg6UjGQjA5WtxO2kq9fO-iW2f9UzDh58etcZ9-X5RSU,21570
37
37
  simo/core/permissions.py,sha256=D8JA3gdsbSfA1Lz6-AIP5ILsYYZ59_Rw4csLqVpuKuE,2928
@@ -64,7 +64,7 @@ simo/core/__pycache__/form_fields.cpython-38.pyc,sha256=u0voKXkA64xbH6LY_-jMBHQS
64
64
  simo/core/__pycache__/forms.cpython-38.pyc,sha256=gDYKULuBTMLxrKfzWXaLDbxuCJvhi2rwR5rIMC_IIVI,17672
65
65
  simo/core/__pycache__/gateways.cpython-38.pyc,sha256=D1ooHL-iSpQrxnD8uAl4xWFJmm-QWZfbkLiLlFOMtdU,4553
66
66
  simo/core/__pycache__/loggers.cpython-38.pyc,sha256=Z-cdQnC6XlIonPV4Sl4E52tP4NMEdPAiHK0cFaIL7I8,1623
67
- simo/core/__pycache__/managers.cpython-38.pyc,sha256=wuZuRuyf_mZwM0DJeA83mewOLunfz4-miA2YEQphE60,3292
67
+ simo/core/__pycache__/managers.cpython-38.pyc,sha256=6RTIxyjOgpQGtAqcUyE2vFPS09w1V5Wmd_vOV7rHRRI,3370
68
68
  simo/core/__pycache__/middleware.cpython-38.pyc,sha256=94jRByEYOhsIuiMgPq_oHrRKrvD8rgQw8plS0n1Mrz8,1933
69
69
  simo/core/__pycache__/models.cpython-38.pyc,sha256=rKg-_vQ-3L3_NKbCd0HIij7Nyev1t7SOz32-6PS4Rds,17907
70
70
  simo/core/__pycache__/permissions.cpython-38.pyc,sha256=pg11ulzmKh4IeWGPIDJ-KYG-S0dKyY5lHh-S3iljb0E,2930
@@ -10191,11 +10191,11 @@ simo/fleet/api.py,sha256=Hxn84xI-Q77HxjINgRbjSJQOv9jii4OL20LxK0VSrS8,2499
10191
10191
  simo/fleet/auto_urls.py,sha256=UX66eR2ykMqFgfIllW-RTdjup5-FieCWl_BVm3CcXKg,702
10192
10192
  simo/fleet/base_types.py,sha256=wL9RVkHr0gA7HI1wZq0pruGEIgvQqpfnCL4cC3ywsvw,102
10193
10193
  simo/fleet/ble.py,sha256=eHA_9ABjbmH1vUVCv9hiPXQL2GZZSEVwfO0xyI1S0nI,1081
10194
- simo/fleet/controllers.py,sha256=ounEUw51X6EYA8xDcGNIik1bDbw4JR3DOGr1FKo4hHs,28829
10195
- simo/fleet/forms.py,sha256=CbrLN_gMCTo8u6fg0ovztkhdYEjHZmSr_3gzsDRMP84,54425
10194
+ simo/fleet/controllers.py,sha256=mASn88pUAFnkmd0UreXh2M0amZpjbv3xMghfTC8OB94,29744
10195
+ simo/fleet/forms.py,sha256=gp95NiQl6Lik-FzY6pSXNAa85VN-HeSOe5vr8Lc2pVc,57219
10196
10196
  simo/fleet/gateways.py,sha256=lKEJW0MgaOEiNnijH50DNSVChvaUT3TA3UurcI57P8k,5677
10197
10197
  simo/fleet/managers.py,sha256=XOpDOA9L-f_550TNSyXnJbun2EmtGz1TenVTMlUSb8E,807
10198
- simo/fleet/models.py,sha256=U4q813VZmYUpgu7Iw4-z80LfHlkZziIqBKqkQzBA9WI,16304
10198
+ simo/fleet/models.py,sha256=t_oi6EYSkg8Y5p3trJPv4MqW6AyUcylge9Bfw83mWCg,16462
10199
10199
  simo/fleet/routing.py,sha256=cofGsVWXMfPDwsJ6HM88xxtRxHwERhJ48Xyxc8mxg5o,149
10200
10200
  simo/fleet/serializers.py,sha256=-16BjY_bp9VbDOYuD0V54h7r_RHpuLNkJX0SydWL9aU,2247
10201
10201
  simo/fleet/socket_consumers.py,sha256=aBNTxvYIw5a5l2ns9x0LnjVJvp4NValEJG4MT4hGAT0,17903
@@ -10208,11 +10208,11 @@ simo/fleet/__pycache__/api.cpython-38.pyc,sha256=rL9fb7cCQatyFvXyKmlNOKmxVo8vHYe
10208
10208
  simo/fleet/__pycache__/auto_urls.cpython-38.pyc,sha256=Tc6a6BCXHjijP8U2jE2ghlJwnSNrGm59-hW5t-80wF0,689
10209
10209
  simo/fleet/__pycache__/base_types.cpython-38.pyc,sha256=deyPwjpT6xZiFxBGFnj5b7R-lbdOTh2krgpJhrcGVhc,274
10210
10210
  simo/fleet/__pycache__/ble.cpython-38.pyc,sha256=Nrof9w7cm4OlpFWHeVnmvvanh2_oF9oQ3TknJiV93-0,1267
10211
- simo/fleet/__pycache__/controllers.cpython-38.pyc,sha256=61dvQDl2nIuB4iaCzdBMwQnly_m0OvI7zteRS6sIm5U,23831
10212
- simo/fleet/__pycache__/forms.cpython-38.pyc,sha256=7B6NZnY6yS8qrmv5zlTa07iUYmdv4nW7PTpOVfRn3OY,37436
10211
+ simo/fleet/__pycache__/controllers.cpython-38.pyc,sha256=CagiILAhdfOsckuO6nh5ukKjAqke4O1m4LSHbAQru5I,24830
10212
+ simo/fleet/__pycache__/forms.cpython-38.pyc,sha256=bdJgWyMfvGtnQiord-SawX8it5OVqJdY21hH5qkPjOQ,38820
10213
10213
  simo/fleet/__pycache__/gateways.cpython-38.pyc,sha256=0RKVn0ndreVKhsrukqeLPSdMnRrsQ_W7yeVeBkRLfIk,5058
10214
10214
  simo/fleet/__pycache__/managers.cpython-38.pyc,sha256=8uz-xpUiqbGDgXIZ_XRZtFb-Tju6NGxflGg-Ee4Yo6k,1310
10215
- simo/fleet/__pycache__/models.cpython-38.pyc,sha256=xmzziQxCWL7TLNhOsHWeIvlJJ4ondGq1Td6cmmDVyvg,13664
10215
+ simo/fleet/__pycache__/models.cpython-38.pyc,sha256=GZ01BjdvTn6_XJBfV8VrSldJ67X06ne-xW4CsQ6N6Wc,13756
10216
10216
  simo/fleet/__pycache__/routing.cpython-38.pyc,sha256=aPrCmxFKVyB8R8ZbJDwdPdFfvT7CvobovvZeq_mqRgY,314
10217
10217
  simo/fleet/__pycache__/serializers.cpython-38.pyc,sha256=9ljhwoHkolcVrJwOVbYCbGPAUKgALRwor_M3W_K0adE,3173
10218
10218
  simo/fleet/__pycache__/socket_consumers.cpython-38.pyc,sha256=oAnUJbrKjhC3-G-o4F-bx3ZztQf7JhmHi-Sh3cm4-4s,13549
@@ -10295,14 +10295,15 @@ simo/fleet/migrations/__pycache__/0035_auto_20240514_0855.cpython-38.pyc,sha256=
10295
10295
  simo/fleet/migrations/__pycache__/0036_auto_20240605_0702.cpython-38.pyc,sha256=SDGibhEg0SYDF6fCte8l8NLChvHKn1qhLIrNZ_WoZQk,1654
10296
10296
  simo/fleet/migrations/__pycache__/0037_alter_colonelpin_options_alter_colonelpin_no_and_more.cpython-38.pyc,sha256=ZappGrTStz3EIHlQ94y5ieXI-SkM9J1ashPey13ncFA,955
10297
10297
  simo/fleet/migrations/__pycache__/__init__.cpython-38.pyc,sha256=5k1KW0jeSDzw6RnVPRq4CaO13Lg7M0F-pxA_gqqZ6Mg,170
10298
- simo/fleet/templates/fleet/controllers_info/button.md,sha256=GIuxqG617174NEtpPeCGVocxO4YMe7-CacgVSu_L5-E,739
10298
+ simo/fleet/templates/fleet/controllers_info/Button.md,sha256=GIuxqG617174NEtpPeCGVocxO4YMe7-CacgVSu_L5-E,739
10299
+ simo/fleet/templates/fleet/controllers_info/ENS160AirQualitySensor.md,sha256=3LSTY9YPFuVPIbVsYCAifcotrXJcOXl2k774_vo6nAE,770
10299
10300
  simo/generic/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10300
10301
  simo/generic/app_widgets.py,sha256=E_pnpA1hxMIhenRCrHoQ5cik06jm2BAHCkl_eo-OudU,1264
10301
10302
  simo/generic/base_types.py,sha256=djymox_boXTHX1BTTCLXrCH7ED-uAsV_idhaDOc3OLI,409
10302
- simo/generic/controllers.py,sha256=gojeCP_vSyLTaP4h56LqqLDykny5lEG-nLclieyVrRg,58292
10303
+ simo/generic/controllers.py,sha256=lnojSqDQ-1FArF2wH_c11y0qd906Wog-qrSG1DoqeXc,58284
10303
10304
  simo/generic/forms.py,sha256=IAfDtmEk1-CP0JBoetOD_ahLm64nK41GOUXjmbUzNtY,29550
10304
10305
  simo/generic/gateways.py,sha256=cc_q_g2HsI2_rWmr8yZ3spnMPwsgoYS5ApWRimc11wU,18831
10305
- simo/generic/models.py,sha256=sa8mFL5i5THMhu9QOTNzBq_KvoChlTyckqO9i1UYw3U,7487
10306
+ simo/generic/models.py,sha256=flpK2jsBFghrvRHzl6IKT-t3WZ-hNOj4ZP2vmBzx0K8,7657
10306
10307
  simo/generic/routing.py,sha256=elQVZmgnPiieEuti4sJ7zITk1hlRxpgbotcutJJgC60,228
10307
10308
  simo/generic/socket_consumers.py,sha256=NfTQGYtVAc864IoogZRxf_0xpDPM0eMCWn0SlKA5P7Y,1751
10308
10309
  simo/generic/__pycache__/__init__.cpython-38.pyc,sha256=mLu54WS9KIl-pHwVCBKpsDFIlOqml--JsOVzAUHg6cU,161
@@ -10311,7 +10312,7 @@ simo/generic/__pycache__/base_types.cpython-38.pyc,sha256=ptw6axyAqemZA35oa6vzr7
10311
10312
  simo/generic/__pycache__/controllers.cpython-38.pyc,sha256=9ZnI6Z084og6Q-9fmhT-PZEM5w_yOC1rwMAHHA6af_Q,36460
10312
10313
  simo/generic/__pycache__/forms.cpython-38.pyc,sha256=v85YEQR9l0QyUgYW_uTKr5qFCjp8TYOAAnfYQvYActI,21227
10313
10314
  simo/generic/__pycache__/gateways.cpython-38.pyc,sha256=8NbsLVDww3Ov5DKF--LKgyrgnrn8yVcKrY21cdvV5aA,13979
10314
- simo/generic/__pycache__/models.cpython-38.pyc,sha256=PzlZsM1jxo3FVb7QDm3bny8UFwTsGrMQe4mj4tJ06eQ,5675
10315
+ simo/generic/__pycache__/models.cpython-38.pyc,sha256=n3FeTMJYh4B6nCPiPKeXiWsUOOWkLHca7qTvfTK6Iik,5844
10315
10316
  simo/generic/__pycache__/routing.cpython-38.pyc,sha256=xtxTUTBTdivzFyA5Wh7k-hUj1WDO_FiRq6HYXdbr9Ks,382
10316
10317
  simo/generic/__pycache__/socket_consumers.cpython-38.pyc,sha256=piFHces0J9QuXu_CNBCQCYjoZEeoaxyVjLfJ9KaR8C8,1898
10317
10318
  simo/generic/static/weather_icons/01d@2x.png,sha256=TZfWi6Rfddb2P-oldWWcjUiuCHiU9Yrc5hyrQAhF26I,948
@@ -10402,15 +10403,16 @@ simo/notifications/migrations/__pycache__/0002_notification_instance.cpython-38.
10402
10403
  simo/notifications/migrations/__pycache__/__init__.cpython-38.pyc,sha256=YMBRHVon2nWDtIUbghckjnC12sIg_ykPWhV5aM0tto4,178
10403
10404
  simo/users/__init__.py,sha256=6a7uBpCWB_DR7p54rbHusc0xvi1qfT1ZCCQGb6TiBh8,52
10404
10405
  simo/users/admin.py,sha256=6RKGnwcrmewJFPzpqnxYn8rxjHO4tJPVFJvA3eMum2s,6746
10405
- simo/users/api.py,sha256=vf11fV8ZCao3Q2TdUO8mDTF3n0N3JYad7CUhz7_ZrZQ,9516
10406
+ simo/users/api.py,sha256=HUY4H9kK_HZKeN4VFERcbNDp6Mmp6p2LdDKBDFvWGUE,10096
10406
10407
  simo/users/apps.py,sha256=cq0A8-U1HALEwev0TicgFhr4CAu7Icz8rwq0HfOaL4E,207
10407
- simo/users/auth_backends.py,sha256=I5pnaTa20-Lxfw_dFG8471xDITb0_fQl1PVhJalp5vU,3992
10408
+ simo/users/auth_backends.py,sha256=bBSNXQJ88TRXaQxyh1aETfmOIfiDr08Jnj8rSY9sHDk,4074
10408
10409
  simo/users/auto_urls.py,sha256=lcJvteBsbHQMJieZpDz-63tDYejLApqsW3CUnDakd7k,272
10409
10410
  simo/users/dynamic_settings.py,sha256=sEIsi4yJw3kH46Jq_aOkSuK7QTfQACGUE-lkyBogCaM,570
10411
+ simo/users/managers.py,sha256=M_51bk9z4jn8e2Ci3pJfIqbf6cRNqfQNSOAg0vPl6Vo,175
10410
10412
  simo/users/middleware.py,sha256=GMCrnWSc_2qCleyQIkfQGdL-pU-UTEcSg1wPvIKZ9uk,1210
10411
- simo/users/models.py,sha256=Y3dkwIvkSbTGWQmAUeK-7ADMsTUU3IqQKOXwtWWzxqY,19824
10413
+ simo/users/models.py,sha256=ud-OOiFuDn8yJK2xrae3Qx-ku9vYBljIHNd3BCHAhM4,19708
10412
10414
  simo/users/permissions.py,sha256=IwtYS8yQdupWbYKR9VimSRDV3qCJ2jXP57Lyjpb2EQM,242
10413
- simo/users/serializers.py,sha256=DwbFGi4WeTYXOSnfrBfd5rC5OGtevYurn27EaTVa1EU,2553
10415
+ simo/users/serializers.py,sha256=a4R408ZgWVbF7OFw4bBfN33Wnn8ljqS8iFcsqmllkWU,2552
10414
10416
  simo/users/sso_urls.py,sha256=gQOaPvGMYFD0NCVSwyoWO-mTEHe5j9sbzV_RK7kdvp0,251
10415
10417
  simo/users/sso_views.py,sha256=-XI67TvQ7SN3goU4OuAHyn84u_1vtusvpn7Pu0K97zo,4648
10416
10418
  simo/users/tasks.py,sha256=HJAqiyWGsaN3wSfquU0UyQ20jL-njXeaaTOdDT3TQ3s,979
@@ -10418,21 +10420,22 @@ simo/users/utils.py,sha256=7gU_TDnAOsDYqJM0CFo8efPah2bTXfGpXxRqzD5RiSs,1270
10418
10420
  simo/users/views.py,sha256=dOQVvmlHG7ihWKJLFUBcqKOA0UDctlMKR0pTc36JZqg,3487
10419
10421
  simo/users/__pycache__/__init__.cpython-38.pyc,sha256=VFoDJE_SKKaPqqYaaBYd1Ndb1hjakkTo_u0EG_XJ1GM,211
10420
10422
  simo/users/__pycache__/admin.cpython-38.pyc,sha256=paoWxwJgOyDF7RT7LIviDqggdELG9-fbydc9UfqHV10,7500
10421
- simo/users/__pycache__/api.cpython-38.pyc,sha256=GcGFVxv0GUcH-TVdvj3v3hty1snKJw3O3-f4PM8DIyM,8305
10423
+ simo/users/__pycache__/api.cpython-38.pyc,sha256=QqQL0MyG8-_7HkqPvqwINuYoco-pJEQ8zT8Crr7t3Rc,8602
10422
10424
  simo/users/__pycache__/apps.cpython-38.pyc,sha256=dgbWL8CxzzISJQTmq_4IztPJ2UzykNVdqA2Ae1PmeGk,605
10423
- simo/users/__pycache__/auth_backends.cpython-38.pyc,sha256=MuOieBIXt6lrDx83-UQtdDyI_U8kE3pU9XR4yFLKBnE,3007
10425
+ simo/users/__pycache__/auth_backends.cpython-38.pyc,sha256=n5nx2QSXNj2idzRcGE6bAagMN-8qxoCs580H1EFZXls,3105
10424
10426
  simo/users/__pycache__/auto_urls.cpython-38.pyc,sha256=K-3sz2h-cEitoflSmZk1t0eUg5mQMMGLNZFREVwG7_o,430
10425
10427
  simo/users/__pycache__/dynamic_settings.cpython-38.pyc,sha256=6F8JBjZkHykySnmZjNEzjS0ijbmPdcp9yUAZ5kqq_Fo,864
10428
+ simo/users/__pycache__/managers.cpython-38.pyc,sha256=C5-diljm874RAFMTkZdcfzPhkHzlUGPAhz2gTvqkDy8,604
10426
10429
  simo/users/__pycache__/middleware.cpython-38.pyc,sha256=Tj4nVEAvxEW3xA63fBRiJWRJpz_M848ZOqbHioc_IPE,1149
10427
- simo/users/__pycache__/models.cpython-38.pyc,sha256=uZ_rkg8_aQsX94HPssGrtZsuxjIrklPnyD46-uPYjFA,18342
10430
+ simo/users/__pycache__/models.cpython-38.pyc,sha256=RiBi5YNHt38s4uUd8Xliswnv7s--v72mjUrBvpal_is,18179
10428
10431
  simo/users/__pycache__/permissions.cpython-38.pyc,sha256=ez5NxoL_JUeeH6GsKhvFreuA3FCBgGf9floSypdXUtM,633
10429
- simo/users/__pycache__/serializers.cpython-38.pyc,sha256=tZzdmCdSnqekAgRl0kyq-msm7QfUA0J_IipfrysAMRM,3477
10432
+ simo/users/__pycache__/serializers.cpython-38.pyc,sha256=PuMy6H0PhEhq89RFmdnFH4pMHB0N3w7opJEFS90JUCY,3477
10430
10433
  simo/users/__pycache__/sso_urls.cpython-38.pyc,sha256=uAwDozpOmrhUald-8tOHANILXkH7-TI8fNYXOtPkSY8,402
10431
10434
  simo/users/__pycache__/sso_views.cpython-38.pyc,sha256=sHEoxLOac3U3Epmhm197huFnW_J3gGCDZSji57itijU,3969
10432
10435
  simo/users/__pycache__/tasks.cpython-38.pyc,sha256=XLMKt3suT7BlcXrJZoH9ZIhhtBuqyiW4lsOB9IbBkko,1225
10433
10436
  simo/users/__pycache__/utils.cpython-38.pyc,sha256=CGaRBk-y9A-8lWWY4bYkI9faAziO0pVYdr5BJNmqbUg,1600
10434
10437
  simo/users/__pycache__/views.cpython-38.pyc,sha256=AXuUnVYRD0ai4FSFDp4qJwryukujAoN6LD3oIj-Cv3o,2426
10435
- simo/users/migrations/0001_initial.py,sha256=5ojkoyTndS2kW5guKK9G9WntXOqbJMyyT_8s_fcJogc,7072
10438
+ simo/users/migrations/0001_initial.py,sha256=_SnJemhNOs8Jjj-PjyvTVCBoxfs5V3lR_4ypUHUdLUg,7017
10436
10439
  simo/users/migrations/0002_componentpermission.py,sha256=rH9pC9HERf_5WWn3LCsNiu03BiHqURTF62pSNfswStI,918
10437
10440
  simo/users/migrations/0003_create_roles_and_system_user.py,sha256=24kNpyPF_DzCRJbx5wi9pvR78YNNNKRJg6TGI8kQiUY,1141
10438
10441
  simo/users/migrations/0004_user_secret_key.py,sha256=ptNOxBVOkkqoowvu_Y9z8uScDzIoq9yokBxIAkG6P5w,491
@@ -10462,7 +10465,7 @@ simo/users/migrations/0027_permissionsrole_can_manage_components.py,sha256=VcGZE
10462
10465
  simo/users/migrations/0028_auto_20240506_1146.py,sha256=7RUFF2rJH-bnPeHwc77p8Q4kEAc3owyG4qp9Kc4aKhU,716
10463
10466
  simo/users/migrations/0029_alter_instanceuser_instance.py,sha256=5ebO0vX9lCnTXBMkWg8633sBCBLNtMLfbocVY-uyQhE,588
10464
10467
  simo/users/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10465
- simo/users/migrations/__pycache__/0001_initial.cpython-38.pyc,sha256=ngXA1QR-Qc2VS-BTTZWybVXiEfifIgKaVS6bADiN8nU,4269
10468
+ simo/users/migrations/__pycache__/0001_initial.cpython-38.pyc,sha256=e4XOKaYRb7l0P7cBnHHi5FQQJMlwjK0g7iqgM-xKmNI,4215
10466
10469
  simo/users/migrations/__pycache__/0002_componentpermission.cpython-38.pyc,sha256=pknJnpic8p6Vdx9DX41FfODXNnvexDswJtUCmC5w1tg,995
10467
10470
  simo/users/migrations/__pycache__/0003_create_roles_and_system_user.cpython-38.pyc,sha256=jcxB_WItrY8APyG_ZiCs3sBvaIVQonm8GSakK82Dc7s,750
10468
10471
  simo/users/migrations/__pycache__/0004_user_secret_key.cpython-38.pyc,sha256=fPLGDwNli7BAThDaXhNWOjJzAzF1wB45hQugim1dcWc,733
@@ -10502,9 +10505,9 @@ simo/users/templates/invitations/expired_msg.html,sha256=47DEQpj8HBSa-_TImW-5JCe
10502
10505
  simo/users/templates/invitations/expired_suggestion.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10503
10506
  simo/users/templates/invitations/taken_msg.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10504
10507
  simo/users/templates/invitations/taken_suggestion.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10505
- simo-2.1.13.dist-info/LICENSE.md,sha256=M7wm1EmMGDtwPRdg7kW4d00h1uAXjKOT3HFScYQMeiE,34916
10506
- simo-2.1.13.dist-info/METADATA,sha256=6OF1UCBLP9AUuLxWH3mUP6FeMEDmOee9WnDow0o-zdY,1848
10507
- simo-2.1.13.dist-info/WHEEL,sha256=-oYQCr74JF3a37z2nRlQays_SX2MqOANoqVjBBAP2yE,91
10508
- simo-2.1.13.dist-info/entry_points.txt,sha256=SJBxiDpH7noO0STxVI_eRIsGR-nLgdXXeqCDe8cXlbM,65
10509
- simo-2.1.13.dist-info/top_level.txt,sha256=GmS1hrAbpVqn9OWZh6UX82eIOdRLgYA82RG9fe8v4Rs,5
10510
- simo-2.1.13.dist-info/RECORD,,
10508
+ simo-2.2.2.dist-info/LICENSE.md,sha256=M7wm1EmMGDtwPRdg7kW4d00h1uAXjKOT3HFScYQMeiE,34916
10509
+ simo-2.2.2.dist-info/METADATA,sha256=Ru0FCahOkylESH_ERFdMhvY_J5YwYXMmIBsjAZvsdKY,1847
10510
+ simo-2.2.2.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
10511
+ simo-2.2.2.dist-info/entry_points.txt,sha256=SJBxiDpH7noO0STxVI_eRIsGR-nLgdXXeqCDe8cXlbM,65
10512
+ simo-2.2.2.dist-info/top_level.txt,sha256=GmS1hrAbpVqn9OWZh6UX82eIOdRLgYA82RG9fe8v4Rs,5
10513
+ simo-2.2.2.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (71.0.3)
2
+ Generator: setuptools (72.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5