simo 2.0.0__py3-none-any.whl → 2.0.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/core/controllers.py CHANGED
@@ -275,6 +275,10 @@ class ControllerBase(ABC):
275
275
  'val': icon if any(values) else None}
276
276
  ]
277
277
 
278
+ def poke(self):
279
+ '''Use this when component is dead to try and wake it up'''
280
+ pass
281
+
278
282
  def _prepare_for_send(self, value):
279
283
  return value
280
284
 
@@ -435,7 +439,19 @@ class BinarySensor(ControllerBase):
435
439
  return value
436
440
 
437
441
 
438
- class Dimmer(ControllerBase, TimerMixin):
442
+ class OnOffPokerMixin:
443
+ _poke_toggle = False
444
+
445
+ def poke(self):
446
+ if self._poke_toggle:
447
+ self._poke_toggle = False
448
+ self.turn_on()
449
+ else:
450
+ self._poke_toggle = True
451
+ self.turn_off()
452
+
453
+
454
+ class Dimmer(ControllerBase, TimerMixin, OnOffPokerMixin):
439
455
  name = _("Dimmer")
440
456
  base_type = 'dimmer'
441
457
  app_widget = KnobWidget
@@ -444,6 +460,7 @@ class Dimmer(ControllerBase, TimerMixin):
444
460
  default_config = {'min': 0.0, 'max': 100.0, 'inverse': False}
445
461
  default_value = 0
446
462
 
463
+
447
464
  def _prepare_for_send(self, value):
448
465
  if isinstance(value, bool):
449
466
  if value:
@@ -484,7 +501,8 @@ class Dimmer(ControllerBase, TimerMixin):
484
501
  self.turn_on()
485
502
 
486
503
 
487
- class DimmerPlus(ControllerBase, TimerMixin):
504
+
505
+ class DimmerPlus(ControllerBase, TimerMixin, OnOffPokerMixin):
488
506
  name = _("Dimmer Plus")
489
507
  base_type = 'dimmer-plus'
490
508
  app_widget = KnobPlusWidget
@@ -564,7 +582,7 @@ class DimmerPlus(ControllerBase, TimerMixin):
564
582
  self.turn_on()
565
583
 
566
584
 
567
- class RGBWLight(ControllerBase, TimerMixin):
585
+ class RGBWLight(ControllerBase, TimerMixin, OnOffPokerMixin):
568
586
  name = _("RGB(W) Light")
569
587
  base_type = 'rgbw-light'
570
588
  app_widget = RGBWidget
@@ -648,7 +666,7 @@ class MultiSwitchBase(ControllerBase):
648
666
  return value
649
667
 
650
668
 
651
- class Switch(MultiSwitchBase, TimerMixin):
669
+ class Switch(MultiSwitchBase, TimerMixin, OnOffPokerMixin):
652
670
  name = _("Switch")
653
671
  base_type = 'switch'
654
672
  app_widget = SingleSwitchWidget
simo/fleet/admin.py CHANGED
@@ -1,3 +1,4 @@
1
+ from threading import Timer
1
2
  from django.contrib import admin
2
3
  from django.utils.safestring import mark_safe
3
4
  from django.template.loader import render_to_string
@@ -78,14 +79,18 @@ class ColonelAdmin(admin.ModelAdmin):
78
79
  return qs
79
80
  return qs.filter(instance__in=request.user.instances)
80
81
 
82
+ def save_model(self, request, obj, form, change):
83
+ super().save_model(request, obj, form, change)
84
+ # give it one second to finish up with atomic transaction and
85
+ # send update_config command.
86
+ def update_colonel_config(colonel):
87
+ colonel.update_config()
88
+ Timer(1, update_colonel_config, [obj]).start()
89
+
90
+
81
91
  def has_add_permission(self, request):
82
92
  return False
83
93
 
84
- def save_model(self, request, obj, form, change):
85
- res = super().save_model(request, obj, form, change)
86
- obj.update_config()
87
- return res
88
-
89
94
  def update_firmware(self, request, queryset):
90
95
  count = 0
91
96
  for colonel in queryset:
simo/fleet/controllers.py CHANGED
@@ -308,6 +308,8 @@ class TTLock(FleeDeviceMixin, Lock):
308
308
  @classmethod
309
309
  def _process_discovery(cls, started_with, data):
310
310
  if data['discover-ttlock'] == 'fail':
311
+ if data['result'] == 0:
312
+ return {'error': 'Internal Colonel error. See Colonel logs.'}
311
313
  if data['result'] == 1:
312
314
  return {'error': 'TTLock not found.'}
313
315
  elif data['result'] == 2:
@@ -7,7 +7,7 @@ from django.db import migrations
7
7
 
8
8
  def forwards_func(apps, schema_editor):
9
9
  from simo.fleet.utils import GPIO_PINS
10
- from django.contrib.contenttypes.models import ContentType
10
+ #from django.contrib.contenttypes.models import ContentType
11
11
 
12
12
  Colonel = apps.get_model('fleet', "Colonel")
13
13
  ColonelPin = apps.get_model("fleet", "ColonelPin")
@@ -26,18 +26,18 @@ def forwards_func(apps, schema_editor):
26
26
  )
27
27
 
28
28
  for i2c in colonel.i2c_interfaces.all():
29
- ct = ContentType.objects.get_for_model(i2c)
29
+ #ct = ContentType.objects.get_for_model(i2c)
30
30
 
31
31
  scl_pin = new_pins[colonel.id][i2c.scl_pin_no]
32
- scl_pin.occupied_by_content_type = ct
33
- scl_pin.occupied_by_id = i2c.id
32
+ #scl_pin.occupied_by_content_type = ct
33
+ #scl_pin.occupied_by_id = i2c.id
34
34
  scl_pin.save()
35
35
  i2c.scl_pin = scl_pin
36
36
  i2c.scl_pin.save()
37
37
 
38
38
  sda_pin = new_pins[colonel.id][i2c.sda_pin_no]
39
- sda_pin.occupied_by_content_type = ct
40
- sda_pin.occupied_by_id = i2c.id
39
+ #sda_pin.occupied_by_content_type = ct
40
+ #sda_pin.occupied_by_id = i2c.id
41
41
  sda_pin.save()
42
42
 
43
43
  i2c.sda_pin = sda_pin
@@ -48,7 +48,7 @@ def forwards_func(apps, schema_editor):
48
48
  for comp in Component.objects.filter(
49
49
  controller_uid__startswith='simo.fleet.'
50
50
  ):
51
- ct = ContentType.objects.get_for_model(comp)
51
+ #ct = ContentType.objects.get_for_model(comp)
52
52
  try:
53
53
  colonel = Colonel.objects.get(id=comp.config['colonel'])
54
54
  except Exception as e:
@@ -64,8 +64,8 @@ def forwards_func(apps, schema_editor):
64
64
 
65
65
  pin = new_pins[colonel.id][comp.config['pin_no']]
66
66
  comp.config['pin'] = pin.pk
67
- pin.occupied_by_content_type = ct
68
- pin.occupied_by_id = comp.id
67
+ #pin.occupied_by_content_type = ct
68
+ #pin.occupied_by_id = comp.id
69
69
  pin.save()
70
70
  if 'power_pin' in comp.config:
71
71
  comp.config['power_pin_no'] = comp.config.pop('power_pin')
@@ -77,8 +77,8 @@ def forwards_func(apps, schema_editor):
77
77
 
78
78
  pin = new_pins[colonel.id][comp.config['power_pin_no']]
79
79
  comp.config['power_pin'] = pin.pk
80
- pin.occupied_by_content_type = ct
81
- pin.occupied_by_id = comp.id
80
+ #pin.occupied_by_content_type = ct
81
+ #pin.occupied_by_id = comp.id
82
82
  pin.save()
83
83
  if 'sensor_pin' in comp.config:
84
84
  comp.config['sensor_pin_no'] = comp.config.pop('sensor_pin')
@@ -90,8 +90,8 @@ def forwards_func(apps, schema_editor):
90
90
 
91
91
  pin = new_pins[colonel.id][comp.config['sensor_pin_no']]
92
92
  comp.config['sensor_pin'] = pin.pk
93
- pin.occupied_by_content_type = ct
94
- pin.occupied_by_id = comp.id
93
+ #pin.occupied_by_content_type = ct
94
+ #pin.occupied_by_id = comp.id
95
95
  pin.save()
96
96
  if 'output_pin' in comp.config:
97
97
  comp.config['output_pin_no'] = comp.config.pop('output_pin')
@@ -103,8 +103,8 @@ def forwards_func(apps, schema_editor):
103
103
 
104
104
  pin = new_pins[colonel.id][comp.config['output_pin_no']]
105
105
  comp.config['output_pin'] = pin.pk
106
- pin.occupied_by_content_type = ct
107
- pin.occupied_by_id = comp.id
106
+ #pin.occupied_by_content_type = ct
107
+ #pin.occupied_by_id = comp.id
108
108
  pin.save()
109
109
  if 'open_pin' in comp.config:
110
110
  comp.config['open_pin_no'] = comp.config.pop('open_pin')
@@ -116,8 +116,8 @@ def forwards_func(apps, schema_editor):
116
116
 
117
117
  pin = new_pins[colonel.id][comp.config['open_pin_no']]
118
118
  comp.config['open_pin'] = pin.pk
119
- pin.occupied_by_content_type = ct
120
- pin.occupied_by_id = comp.id
119
+ #pin.occupied_by_content_type = ct
120
+ #pin.occupied_by_id = comp.id
121
121
  pin.save()
122
122
  if 'close_pin' in comp.config:
123
123
  comp.config['close_pin_no'] = comp.config.pop('close_pin')
@@ -129,8 +129,8 @@ def forwards_func(apps, schema_editor):
129
129
 
130
130
  pin = new_pins[colonel.id][comp.config['close_pin_no']]
131
131
  comp.config['close_pin'] = pin.pk
132
- pin.occupied_by_content_type = ct
133
- pin.occupied_by_id = comp.id
132
+ #pin.occupied_by_content_type = ct
133
+ #pin.occupied_by_id = comp.id
134
134
  pin.save()
135
135
 
136
136
  if 'controls' in comp.config:
@@ -146,8 +146,8 @@ def forwards_func(apps, schema_editor):
146
146
  pin = new_pins[colonel.id][updated_controls['pin_no']]
147
147
  updated_controls['pin'] = pin.pk
148
148
 
149
- pin.occupied_by_content_type = ct
150
- pin.occupied_by_id = comp.id
149
+ #pin.occupied_by_content_type = ct
150
+ #pin.occupied_by_id = comp.id
151
151
  pin.save()
152
152
 
153
153
  comp.config['controls'][i] = updated_controls
simo/fleet/models.py CHANGED
@@ -82,8 +82,10 @@ class Colonel(DirtyFieldsMixin, models.Model):
82
82
  occupied_pins = models.JSONField(default=dict, blank=True)
83
83
 
84
84
  logs_stream = models.BooleanField(
85
- default=False, help_text="Might cause unnecessary overhead. "
86
- "Better to leave this off if things are running smoothly."
85
+ default=False, help_text="ATENTION! Causes serious overhead!!! "
86
+ "Leave this off. "
87
+ "It causes Colonel websocket to run out of memory "
88
+ "and reset if a lot of data is being transmitted."
87
89
  )
88
90
  pwm_frequency = models.IntegerField(default=1, choices=(
89
91
  (0, "3kHz"), (1, "22kHz")
@@ -161,10 +163,8 @@ class Colonel(DirtyFieldsMixin, models.Model):
161
163
  @transaction.atomic
162
164
  def rebuild_occupied_pins(self):
163
165
  for pin in ColonelPin.objects.filter(colonel=self):
164
- if isinstance(pin.occupied_by, Component):
165
- pin.occupied_by_content_type = None
166
- pin.occupied_by_id = None
167
- pin.save()
166
+ pin.occupied_by = None
167
+ pin.save()
168
168
 
169
169
  for component in self.components.all():
170
170
  try:
@@ -176,9 +176,16 @@ class Colonel(DirtyFieldsMixin, models.Model):
176
176
  pin.occupied_by = component
177
177
  pin.save()
178
178
 
179
+ for interface in self.i2c_interfaces.all():
180
+ interface.sda_pin.occupied_by = interface
181
+ interface.sda_pin.save()
182
+ interface.scl_pin.occupied_by = interface
183
+ interface.scl_pin.save()
184
+
179
185
 
180
186
  @transaction.atomic()
181
187
  def move_to(self, other_colonel):
188
+ # TODO: Need to replace pins on components!
182
189
  other_colonel.refresh_from_db()
183
190
  assert list(other_colonel.components.all()) == [], \
184
191
  "Other colonel must be completely empty!"
@@ -196,6 +203,7 @@ class Colonel(DirtyFieldsMixin, models.Model):
196
203
 
197
204
  for i2c_interface in self.i2c_interfaces.all():
198
205
  I2CInterface.objects.create(
206
+ no=i2c_interface.no,
199
207
  colonel=other_colonel, name=i2c_interface.name,
200
208
  freq=i2c_interface.freq,
201
209
  scl_pin=ColonelPin.objects.get(
@@ -261,22 +269,21 @@ class ColonelPin(models.Model):
261
269
 
262
270
 
263
271
  @receiver(post_save, sender=Colonel)
264
- def create_interfaces(sender, instance, created, *args, **kwargs):
265
- if not created:
266
- return
267
- for no, data in GPIO_PINS.get(instance.type).items():
268
- ColonelPin.objects.get_or_create(
269
- colonel=instance, no=no,
270
- input=data.get('input'), output=data.get('output'),
271
- capacitive=data.get('capacitive'), adc=data.get('adc'),
272
- native=data.get('native'), note=data.get('note')
273
- )
274
- if instance.type in ('ample-wall', 'game-changer'):
275
- I2CInterface.objects.create(
276
- colonel=instance, name='Main', no=0,
277
- scl_pin=ColonelPin.objects.get(colonel=instance, no=4),
278
- sda_pin=ColonelPin.objects.get(colonel=instance, no=15),
279
- )
272
+ def after_colonel_save(sender, instance, created, *args, **kwargs):
273
+ if created:
274
+ for no, data in GPIO_PINS.get(instance.type).items():
275
+ ColonelPin.objects.get_or_create(
276
+ colonel=instance, no=no,
277
+ input=data.get('input'), output=data.get('output'),
278
+ capacitive=data.get('capacitive'), adc=data.get('adc'),
279
+ native=data.get('native'), note=data.get('note')
280
+ )
281
+ if instance.type in ('ample-wall', 'game-changer'):
282
+ I2CInterface.objects.create(
283
+ colonel=instance, name='Main', no=0,
284
+ scl_pin=ColonelPin.objects.get(colonel=instance, no=4),
285
+ sda_pin=ColonelPin.objects.get(colonel=instance, no=15),
286
+ )
280
287
 
281
288
 
282
289
  @receiver(pre_delete, sender=Component)
simo/fleet/serializers.py CHANGED
@@ -45,3 +45,8 @@ class ColonelSerializer(serializers.ModelSerializer):
45
45
  for pin in obj.pins.all():
46
46
  result.append(ColonelPinSerializer(pin).data)
47
47
  return result
48
+
49
+ def update(self, instance, validated_data):
50
+ instance = super().update(instance, validated_data)
51
+ instance.update_config()
52
+ return instance
simo/generic/forms.py CHANGED
@@ -167,6 +167,12 @@ class AlarmGroupConfigForm(BaseComponentForm):
167
167
  required=False,
168
168
  help_text="Defines if this is your main/top global alarm group."
169
169
  )
170
+ notify_on_breach = forms.IntegerField(
171
+ required=False, min_value=0,
172
+ help_text="Notify active users if "
173
+ "not disarmed within given number of seconds "
174
+ "after the breached."
175
+ )
170
176
  has_alarm = False
171
177
 
172
178
 
simo/generic/models.py CHANGED
@@ -1,6 +1,4 @@
1
- from django.db import models
2
1
  from threading import Timer
3
- from django.utils.translation import gettext_lazy as _
4
2
  from django.db.models.signals import pre_save, post_save, post_delete
5
3
  from django.dispatch import receiver
6
4
  from simo.core.models import Instance, Component
@@ -20,7 +18,8 @@ def handle_alarm_groups(sender, instance, *args, **kwargs):
20
18
 
21
19
  for alarm_group in Component.objects.filter(
22
20
  controller_uid=AlarmGroup.uid,
23
- config__components__contains=instance.id
21
+ config__components__contains=instance.id,
22
+ config__notify_on_breach__gt=-1
24
23
  ).exclude(value='disarmed'):
25
24
  stats = {
26
25
  'disarmed': 0, 'pending-arm': 0, 'armed': 0, 'breached': 0
@@ -33,7 +32,6 @@ def handle_alarm_groups(sender, instance, *args, **kwargs):
33
32
  alarm_group.config['stats'] = stats
34
33
  alarm_group.save(update_fields=['config'])
35
34
 
36
- alarm_group_value = alarm_group.value
37
35
  if stats['disarmed'] == len(alarm_group.config['components']):
38
36
  alarm_group_value = 'disarmed'
39
37
  elif stats['armed'] == len(alarm_group.config['components']):
@@ -41,9 +39,11 @@ def handle_alarm_groups(sender, instance, *args, **kwargs):
41
39
  elif stats['breached']:
42
40
  if alarm_group.value != 'breached':
43
41
  def notify_users_security_breach(alarm_group_component_id):
44
- from simo.notifications.utils import notify_users
45
- alarm_group_component = Component.objects.get(
46
- id=alarm_group_component_id)
42
+ alarm_group_component = Component.objects.filter(
43
+ id=alarm_group_component_id, value='breached'
44
+ ).first()
45
+ if not alarm_group_component:
46
+ return
47
47
  breached_components = Component.objects.filter(
48
48
  pk__in=alarm_group_component.config['components'],
49
49
  arm_status='breached'
@@ -51,16 +51,22 @@ def handle_alarm_groups(sender, instance, *args, **kwargs):
51
51
  body = "Security Breach! " + '; '.join(
52
52
  [str(c) for c in breached_components]
53
53
  )
54
+ from simo.notifications.utils import notify_users
54
55
  notify_users(
56
+ alarm_group_component.zone.instance,
55
57
  'alarm', str(alarm_group_component), body,
56
58
  component=alarm_group_component
57
59
  )
58
- t = Timer(1, notify_users_security_breach, [alarm_group.id])
60
+ t = Timer(
61
+ # give it one second to finish with other db processes.
62
+ alarm_group.config['notify_on_breach'] + 1,
63
+ notify_users_security_breach, [alarm_group.id]
64
+ )
59
65
  t.start()
60
66
  alarm_group_value = 'breached'
61
67
  else:
62
68
  alarm_group_value = 'pending-arm'
63
- alarm_group.set(alarm_group_value)
69
+ alarm_group.controller.set(alarm_group_value)
64
70
 
65
71
 
66
72
  @receiver(post_save, sender=Component)
@@ -14,7 +14,7 @@ def notify_users(instance, severity, title, body=None, component=None, users=Non
14
14
  )
15
15
  if not users:
16
16
  users = User.objects.filter(
17
- instance_roles__instnace=instance,
17
+ instance_roles__instance=instance,
18
18
  instance_roles__is_active=True
19
19
  )
20
20
  for user in users:
simo/users/serializers.py CHANGED
@@ -81,8 +81,8 @@ class FingerprintSerializer(serializers.ModelSerializer):
81
81
 
82
82
  class Meta:
83
83
  model = Fingerprint
84
- fields = 'type', 'value', 'user'
85
- read_only_fields = ('type', 'value')
84
+ fields = 'id', 'type', 'value', 'user'
85
+ read_only_fields = ('id', 'type', 'value')
86
86
 
87
87
  def get_type(self, obj):
88
88
  return obj.type
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: simo
3
- Version: 2.0.0
3
+ Version: 2.0.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
@@ -34,7 +34,7 @@ simo/core/auto_urls.py,sha256=0gu-IL7PHobrmKW6ksffiOkAYu-aIorykWdxRNtwGYo,1194
34
34
  simo/core/autocomplete_views.py,sha256=JT5LA2_Wtr60XYSAIqaXFKFYPjrmkEf6yunXD9y2zco,4022
35
35
  simo/core/base_types.py,sha256=yqbIZqBksrAkEuHRbt6iExwPDDy0K5II2NzRCkmOvMU,589
36
36
  simo/core/context.py,sha256=98PXAMie43faRVBFkOG22uNpvGRNprcGhzjBFkrxaRY,1367
37
- simo/core/controllers.py,sha256=vJ4PXlmo8gZ7_Cm16IV9fvgObolkC-0P7dgujavvXnE,26200
37
+ simo/core/controllers.py,sha256=bxJRpmREw8CsgDAiuVvpOl8HxHhZqrRM1qXTF_8afV4,26617
38
38
  simo/core/dynamic_settings.py,sha256=U2WNL96JzVXdZh0EqMPWrxqO6BaRR2Eo5KTDqz7MC4o,1943
39
39
  simo/core/events.py,sha256=LvtonJGNyCb6HLozs4EG0WZItnDwNdtnGQ4vTcnKvUs,4438
40
40
  simo/core/filters.py,sha256=ghtOZcrwNAkIyF5_G9Sn73NkiI71mXv0NhwCk4IyMIM,411
@@ -10120,17 +10120,17 @@ simo/core/utils/validators.py,sha256=FRO6_K5HAO1OaC-LosApZjh-W3EA-IJZ53HnwEJgqiI
10120
10120
  simo/core/utils/__pycache__/api.cpython-38.pyc,sha256=CuJq9GKQC8gbeCxmH2wQHZUmkIihVILSEIAoVYCFwH0,1575
10121
10121
  simo/core/utils/__pycache__/serialization.cpython-38.pyc,sha256=zOo2M97bAC0Ed-iiNoVtcHvgdAYR8RwrF2bUwuf8Pus,1145
10122
10122
  simo/fleet/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10123
- simo/fleet/admin.py,sha256=9dEA3XcynAhcyeJrGzuPOXSGGPtawVyHUOyrVty5CEc,5480
10123
+ simo/fleet/admin.py,sha256=Vxp3yTNRVoDif_hwwBW3weeMNakQ3iZo-GC0VvdScfQ,5699
10124
10124
  simo/fleet/api.py,sha256=Hxn84xI-Q77HxjINgRbjSJQOv9jii4OL20LxK0VSrS8,2499
10125
10125
  simo/fleet/auto_urls.py,sha256=gAXTWUvsWkQHRdZGM_W_5iJBEsM4lY063kIx3f5LUqs,578
10126
10126
  simo/fleet/ble.py,sha256=eHA_9ABjbmH1vUVCv9hiPXQL2GZZSEVwfO0xyI1S0nI,1081
10127
- simo/fleet/controllers.py,sha256=ELYfj2o1xrceg2tcprv6ofHA4WtYaWrFmgiIAthnCSw,13885
10127
+ simo/fleet/controllers.py,sha256=N8Qzdp2RPFrpZ_l9O4u8VjHWoY_WTWGg76s3V3oJqEs,13999
10128
10128
  simo/fleet/forms.py,sha256=UGj1mK2Zbl2LRlvLtEDObeGfC2wcuHleRbePo1_Vx6I,34972
10129
10129
  simo/fleet/gateways.py,sha256=xFsmF_SXYXK_kMJOCHkiInPJ_0VcPWz-kJDoMup2lT8,1576
10130
10130
  simo/fleet/managers.py,sha256=kpfvvfdH4LDxddIBDpdAb5gsVk8Gb0-L9biFcj9OFPs,807
10131
- simo/fleet/models.py,sha256=ly_1nU0rzLcplkQe7pBL-7BweRiaKOj0XunyvQSQIno,12216
10131
+ simo/fleet/models.py,sha256=6IzGv4jvLaRInzKX8cetHcD08f59HCLWRClYDQJJyKc,12588
10132
10132
  simo/fleet/routing.py,sha256=cofGsVWXMfPDwsJ6HM88xxtRxHwERhJ48Xyxc8mxg5o,149
10133
- simo/fleet/serializers.py,sha256=lVxqb4ldmJ5bUIklqeet5AIQak2Fbp_Tx5uKcP0eqmQ,1339
10133
+ simo/fleet/serializers.py,sha256=zEpXAXxjk4Rf1JhlNnLTrs20qJggqjvIySbeHVo4Tt4,1505
10134
10134
  simo/fleet/socket_consumers.py,sha256=HbkrV0i1TwBC38otu_2lzN6IlBdyZHVdXIVhU4y-YCM,18872
10135
10135
  simo/fleet/utils.py,sha256=D0EGFbDmW8zyhyxf5ozGtRpo4Sy5Ov6ZixukBK_e2Do,3462
10136
10136
  simo/fleet/views.py,sha256=PbdZpsM_7-oyKzuDX1A5WULNABA1_B7ISF70UJX97FE,1662
@@ -10175,7 +10175,7 @@ simo/fleet/migrations/0023_colonel_is_authorized.py,sha256=IoyPUR45axv9V6QsntPTj
10175
10175
  simo/fleet/migrations/0024_colonel_pwm_frequency.py,sha256=nfTDs3GeIEkkiuQMB3_tc8TuyZSNOdQ2KhnJKTtQ9XE,498
10176
10176
  simo/fleet/migrations/0025_auto_20240130_1334.py,sha256=dHcQnlF7LBp6ikx_s5AB_I19sQ-iMB8XJ1X1yoWVvBs,825
10177
10177
  simo/fleet/migrations/0026_rename_i2cinterface_scl_pin_and_more.py,sha256=SEQxhgFsxWaUmfXaMOHHj0q8EqF9bt9PTOVuVa6wrQs,2752
10178
- simo/fleet/migrations/0027_auto_20240306_0802.py,sha256=ZWttZ54aoSrLBliHOiF9RL380gdNSlCEzlGJLX7fUnk,5951
10178
+ simo/fleet/migrations/0027_auto_20240306_0802.py,sha256=kOtLlCbbkqZu1zwzNZyA5rE_0TifF7tURdLCu_hzTLE,5972
10179
10179
  simo/fleet/migrations/0028_remove_i2cinterface_scl_pin_no_and_more.py,sha256=CeoFQPIu3jb4Gtyu5FhRjhl_mKhowMoEdtFU1oWD4UE,450
10180
10180
  simo/fleet/migrations/0029_alter_i2cinterface_scl_pin_and_more.py,sha256=aquymc1ZcUVgdrqZxl3viN6kiCHtw4HjgL6n4d0cSpQ,886
10181
10181
  simo/fleet/migrations/0030_colonelpin_label_alter_colonel_type.py,sha256=5T5bmQxPZrG0UseCLd7ssV-AeFF3O4T_DFxLu3whSqg,706
@@ -10217,9 +10217,9 @@ simo/generic/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10217
10217
  simo/generic/app_widgets.py,sha256=E_pnpA1hxMIhenRCrHoQ5cik06jm2BAHCkl_eo-OudU,1264
10218
10218
  simo/generic/base_types.py,sha256=djymox_boXTHX1BTTCLXrCH7ED-uAsV_idhaDOc3OLI,409
10219
10219
  simo/generic/controllers.py,sha256=Fs95mRBJgT5ZNMnxxwQJ_94TJ7Y_O0IygWSgfjLZzU0,51912
10220
- simo/generic/forms.py,sha256=KVGxTOqlqN9qSMrVBF2Mz2U39xZ_4s5ULHFprvAttb4,19959
10220
+ simo/generic/forms.py,sha256=HYctQMudlMpQRxkI67C6NShaM1dAzo0J-qbu-AFpozU,20194
10221
10221
  simo/generic/gateways.py,sha256=aHU0mA0ADNVzw3EXb8paXI5DI-gNd4CrGtaV5qDhILY,15499
10222
- simo/generic/models.py,sha256=-y3DQmD1k5uGB4cwchrE2PL61217BL6quCjkUWV3x3k,3340
10222
+ simo/generic/models.py,sha256=d00Q-UXtt7mG9MdPpZ3mXnxKjwlTI2FgCEklZpGBk7s,3629
10223
10223
  simo/generic/routing.py,sha256=elQVZmgnPiieEuti4sJ7zITk1hlRxpgbotcutJJgC60,228
10224
10224
  simo/generic/socket_consumers.py,sha256=NfTQGYtVAc864IoogZRxf_0xpDPM0eMCWn0SlKA5P7Y,1751
10225
10225
  simo/generic/static/weather_icons/01d@2x.png,sha256=TZfWi6Rfddb2P-oldWWcjUiuCHiU9Yrc5hyrQAhF26I,948
@@ -10266,7 +10266,7 @@ simo/notifications/admin.py,sha256=y_gmHYXbDh98LUUa-lp9DilTIgM6-pIujWPQPLQsJo8,8
10266
10266
  simo/notifications/api.py,sha256=GXQpq68ULBaJpU8w3SJKaCKuxYGWYehKnGeocGB1RVc,1783
10267
10267
  simo/notifications/models.py,sha256=VZcvweii59j89nPKlWeUSJ44Qz3ZLjJ6mXN6uB9F1Sw,2506
10268
10268
  simo/notifications/serializers.py,sha256=altDEAPWwOhxRcEzE9-34jL8EFpyf3vPoEdAPoVLfGc,523
10269
- simo/notifications/utils.py,sha256=M2UFufqAWD64kHw0rUDIr4nJ1ZkHd47sG_TVa4j918s,907
10269
+ simo/notifications/utils.py,sha256=5CtKOvX0vbWLXFvJD_8WfWulMEp1FuMwrfCGDLHySdA,907
10270
10270
  simo/notifications/migrations/0001_initial.py,sha256=Zh69AQ-EKlQKfqfnMDVRcxvo1MxRY-TFLCdnNcgqi6g,2003
10271
10271
  simo/notifications/migrations/0002_notification_instance.py,sha256=B3msbMeKvsuq-V7gvRADRjj5PFLayhi3pQvHZjqzO5g,563
10272
10272
  simo/notifications/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -10279,7 +10279,7 @@ simo/users/dynamic_settings.py,sha256=sEIsi4yJw3kH46Jq_aOkSuK7QTfQACGUE-lkyBogCa
10279
10279
  simo/users/middleware.py,sha256=9epN8xDcnYAMoEjAeJGg4W9e54szTgh48LKz3rlywFI,1287
10280
10280
  simo/users/models.py,sha256=f59mkQBpq_fFLDUa-44b1qWleB-TvGdC1M0G67huJMg,18498
10281
10281
  simo/users/permissions.py,sha256=IwtYS8yQdupWbYKR9VimSRDV3qCJ2jXP57Lyjpb2EQM,242
10282
- simo/users/serializers.py,sha256=9fDT23WBsOSPnNlmtOp6095IF8wng2eg_iboUCzYIDA,2474
10282
+ simo/users/serializers.py,sha256=yrWghK7ONxfzkjO3LbDLK1WPxsPBMYUegdm29qovjjo,2486
10283
10283
  simo/users/sso_urls.py,sha256=gQOaPvGMYFD0NCVSwyoWO-mTEHe5j9sbzV_RK7kdvp0,251
10284
10284
  simo/users/sso_views.py,sha256=-XI67TvQ7SN3goU4OuAHyn84u_1vtusvpn7Pu0K97zo,4648
10285
10285
  simo/users/tasks.py,sha256=v9J7t4diB0VnqUDVZAQ8H-rlr4ZR14bgEUuEGpODyOI,854
@@ -10343,8 +10343,8 @@ simo/users/templates/invitations/expired_msg.html,sha256=47DEQpj8HBSa-_TImW-5JCe
10343
10343
  simo/users/templates/invitations/expired_suggestion.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10344
10344
  simo/users/templates/invitations/taken_msg.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10345
10345
  simo/users/templates/invitations/taken_suggestion.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10346
- simo-2.0.0.dist-info/LICENSE.md,sha256=M7wm1EmMGDtwPRdg7kW4d00h1uAXjKOT3HFScYQMeiE,34916
10347
- simo-2.0.0.dist-info/METADATA,sha256=a3T7zOscRzMDFgziDrgeASopaHXZ8XnLBEwdS4OTl08,1669
10348
- simo-2.0.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
10349
- simo-2.0.0.dist-info/top_level.txt,sha256=GmS1hrAbpVqn9OWZh6UX82eIOdRLgYA82RG9fe8v4Rs,5
10350
- simo-2.0.0.dist-info/RECORD,,
10346
+ simo-2.0.2.dist-info/LICENSE.md,sha256=M7wm1EmMGDtwPRdg7kW4d00h1uAXjKOT3HFScYQMeiE,34916
10347
+ simo-2.0.2.dist-info/METADATA,sha256=svTddp9-mP3qTEeGmU9RQ__HdbwqgbY6ivmcGiRa4xg,1669
10348
+ simo-2.0.2.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
10349
+ simo-2.0.2.dist-info/top_level.txt,sha256=GmS1hrAbpVqn9OWZh6UX82eIOdRLgYA82RG9fe8v4Rs,5
10350
+ simo-2.0.2.dist-info/RECORD,,
File without changes