simo 2.0.7__py3-none-any.whl → 2.0.9__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.

Binary file
simo/core/models.py CHANGED
@@ -267,29 +267,38 @@ class Gateway(DirtyFieldsMixin, models.Model, SimoAdminMixin):
267
267
 
268
268
  def process_discovery(self, data):
269
269
  self.refresh_from_db()
270
+ if self.discovery.get('finished'):
271
+ print(
272
+ f"Gateway is not in pairing mode at the moment!"
273
+ )
274
+ return
275
+ if self.discovery['controller_uid'] != data.get('type'):
276
+ print(f"Gateway is not in pairing mode for {self.discovery['controller_uid']} "
277
+ f"but not for {data.get('type')} at the moment!")
278
+ return
279
+
270
280
  from .utils.type_constants import CONTROLLER_TYPES_MAP
271
- ControllerClass = CONTROLLER_TYPES_MAP.get(
272
- self.discovery['controller_uid']
281
+ ControllerClass = CONTROLLER_TYPES_MAP.get(data.get('type'))
282
+ if not hasattr(ControllerClass, '_process_discovery'):
283
+ print(f"{data.get('type')} controller has no _process_discovery method." )
284
+ return
285
+
286
+ result = ControllerClass._process_discovery(
287
+ started_with=self.discovery['init_data'], data=data
273
288
  )
274
- if ControllerClass and hasattr(
275
- ControllerClass, '_process_discovery'
276
- ):
277
- result = ControllerClass._process_discovery(
278
- started_with=self.discovery['init_data'], data=data
279
- )
280
- if result:
281
- self.refresh_from_db()
282
- if not isinstance(result, dict) and isinstance(result, Iterable):
283
- for res in result:
284
- if isinstance(res, models.Model):
285
- self.discovery['result'].append(res.pk)
286
- else:
287
- self.discovery['result'].append(res)
288
- else:
289
- if isinstance(result, models.Model):
290
- self.discovery['result'].append(result.pk)
289
+ if result:
290
+ self.refresh_from_db()
291
+ if not isinstance(result, dict) and isinstance(result, Iterable):
292
+ for res in result:
293
+ if isinstance(res, models.Model):
294
+ self.discovery['result'].append(res.pk)
291
295
  else:
292
- self.discovery['result'].append(result)
296
+ self.discovery['result'].append(res)
297
+ else:
298
+ if isinstance(result, models.Model):
299
+ self.discovery['result'].append(result.pk)
300
+ else:
301
+ self.discovery['result'].append(result)
293
302
 
294
303
  self.save(update_fields=['discovery'])
295
304
 
Binary file
Binary file
@@ -0,0 +1,5 @@
1
+ from django.utils.translation import gettext_lazy as _
2
+
3
+ BASE_TYPES = {
4
+ 'dali': _("Dali Device"),
5
+ }
simo/fleet/controllers.py CHANGED
@@ -8,7 +8,7 @@ from simo.core.controllers import (
8
8
  )
9
9
  from simo.conf import dynamic_settings
10
10
  from simo.core.app_widgets import NumericSensorWidget
11
- from simo.core.controllers import Lock
11
+ from simo.core.controllers import Lock, ControllerBase, SingleSwitchWidget
12
12
  from simo.core.utils.helpers import heat_index
13
13
  from simo.core.utils.serialization import (
14
14
  serialize_form_data, deserialize_form_data
@@ -24,7 +24,7 @@ from .forms import (
24
24
  ColonelDHTSensorConfigForm, DS18B20SensorConfigForm,
25
25
  BME680SensorConfigForm, MPC9808SensorConfigForm,
26
26
  DualMotorValveForm, BlindsConfigForm, BurglarSmokeDetectorConfigForm,
27
- TTLockConfigForm, DALIDeviceConfigForm
27
+ TTLockConfigForm, DALIDeviceConfigForm, DaliSwitchForm
28
28
  )
29
29
 
30
30
 
@@ -91,14 +91,14 @@ class BurglarSmokeDetector(BinarySensor):
91
91
  ]
92
92
 
93
93
 
94
- class AnalogSensor(FleeDeviceMixin, BasicSensorMixin, BaseNumericSensor):
95
- config_form = ColonelNumericSensorConfigForm
96
- name = "Analog sensor"
97
-
98
- def _get_occupied_pins(self):
99
- return [
100
- self.component.config['pin_no'],
101
- ]
94
+ # class AnalogSensor(FleeDeviceMixin, BasicSensorMixin, BaseNumericSensor):
95
+ # config_form = ColonelNumericSensorConfigForm
96
+ # name = "Analog sensor"
97
+ #
98
+ # def _get_occupied_pins(self):
99
+ # return [
100
+ # self.component.config['pin_no'],
101
+ # ]
102
102
 
103
103
 
104
104
  class DS18B20Sensor(FleeDeviceMixin, BasicSensorMixin, BaseNumericSensor):
@@ -112,6 +112,7 @@ class DS18B20Sensor(FleeDeviceMixin, BasicSensorMixin, BaseNumericSensor):
112
112
 
113
113
 
114
114
  class BaseClimateSensor(FleeDeviceMixin, BasicSensorMixin, BaseMultiSensor):
115
+ manual_add = False
115
116
  app_widget = NumericSensorWidget
116
117
 
117
118
  def __init__(self, *args, **kwargs):
@@ -196,7 +197,7 @@ class Switch(FleeDeviceMixin, BasicOutputMixin, BaseSwitch):
196
197
 
197
198
 
198
199
  class PWMOutput(FleeDeviceMixin, BasicOutputMixin, BaseDimmer):
199
- name = "PWM Output"
200
+ name = "Dimmer"
200
201
  config_form = ColonelPWMOutputConfigForm
201
202
 
202
203
  def _prepare_for_send(self, value):
@@ -303,7 +304,7 @@ class TTLock(FleeDeviceMixin, Lock):
303
304
  )
304
305
  GatewayObjectCommand(
305
306
  gateway, form_cleaned_data['colonel'],
306
- command='discover-ttlock',
307
+ command='discover', type=self.uid
307
308
  ).publish()
308
309
 
309
310
  @classmethod
@@ -385,6 +386,14 @@ class TTLock(FleeDeviceMixin, Lock):
385
386
  command='call', method='delete_code', args=[str(code)]
386
387
  ).publish()
387
388
 
389
+ def clear_codes(self):
390
+ GatewayObjectCommand(
391
+ self.component.gateway,
392
+ Colonel(id=self.component.config['colonel']),
393
+ id=self.component.id,
394
+ command='call', method='clear_codes'
395
+ ).publish()
396
+
388
397
  def get_codes(self):
389
398
  GatewayObjectCommand(
390
399
  self.component.gateway,
@@ -410,6 +419,14 @@ class TTLock(FleeDeviceMixin, Lock):
410
419
  command='call', method='delete_fingerprint', args=[str(code)]
411
420
  ).publish()
412
421
 
422
+ def clear_fingerprints(self):
423
+ GatewayObjectCommand(
424
+ self.component.gateway,
425
+ Colonel(id=self.component.config['colonel']),
426
+ id=self.component.id,
427
+ command='call', method='clear_fingerprints'
428
+ ).publish()
429
+
413
430
  def get_fingerprints(self):
414
431
  GatewayObjectCommand(
415
432
  self.component.gateway,
@@ -419,11 +436,19 @@ class TTLock(FleeDeviceMixin, Lock):
419
436
  ).publish()
420
437
 
421
438
 
422
- class DALIDevice(FleeDeviceMixin):
439
+ class DALIDevice(FleeDeviceMixin, ControllerBase):
423
440
  gateway_class = FleetGatewayHandler
424
441
  config_form = DALIDeviceConfigForm
442
+ name = "DALI Device"
425
443
  discovery_msg = _("Please hook up your new DALI device to your DALI bus.")
426
444
 
445
+ base_type = 'dali'
446
+ default_value = False
447
+ app_widget = SingleSwitchWidget
448
+
449
+ def _validate_val(self, value, occasion=None):
450
+ pass
451
+
427
452
  @classmethod
428
453
  def init_discovery(self, form_cleaned_data):
429
454
  from simo.core.models import Gateway
@@ -434,14 +459,25 @@ class DALIDevice(FleeDeviceMixin):
434
459
  )
435
460
  GatewayObjectCommand(
436
461
  gateway, form_cleaned_data['colonel'],
437
- command=f'discover-dali',
438
- interface=form_cleaned_data['interface'].no
462
+ command='discover', type=self.uid,
463
+ i=form_cleaned_data['interface'].no
439
464
  ).publish()
440
465
 
441
466
  @classmethod
442
467
  def _process_discovery(cls, started_with, data):
468
+ if data['discovery-result'] == 'fail':
469
+ if data['result'] == 1:
470
+ return {'error': 'DALI interface is unavailable!'}
471
+ else:
472
+ return {'error': 'Unknown error!'}
473
+
474
+ controller_cls = globals().get(data['result']['type'])
475
+
443
476
  started_with = deserialize_form_data(started_with)
444
- form = TTLockConfigForm(controller_uid=cls.uid, data=started_with)
477
+ started_with['name'] += f" {data['result']['config']['da']}"
478
+ form = controller_cls.config_form(
479
+ controller_uid=controller_cls.uid, data=started_with
480
+ )
445
481
 
446
482
  if form.is_valid():
447
483
  new_component = form.save()
@@ -452,11 +488,9 @@ class DALIDevice(FleeDeviceMixin):
452
488
  'config': {
453
489
  'type': cls.uid.split('.')[-1],
454
490
  'config': new_component.config,
455
- 'val': False,
456
491
  },
457
492
  }
458
493
  new_component.save()
459
- new_component.gateway.finish_discovery()
460
494
  GatewayObjectCommand(
461
495
  new_component.gateway, Colonel(
462
496
  id=new_component.config['colonel']
@@ -470,6 +504,7 @@ class DALIDevice(FleeDeviceMixin):
470
504
 
471
505
 
472
506
  class DALIGear(DALIDevice):
507
+ manual_add = False
473
508
 
474
509
  def _send_to_device(self, value):
475
510
  GatewayObjectCommand(
@@ -481,13 +516,15 @@ class DALIGear(DALIDevice):
481
516
 
482
517
 
483
518
  class DALILamp(DALIGear, BaseSwitch):
519
+ family = 'dali'
484
520
  manual_add = False
485
521
  name = 'DALI Lamp'
486
- discovery_msg = _("Please hook up your new DALI device to your DALI bus.")
522
+ config_form = DaliSwitchForm
487
523
 
488
524
 
489
525
  class DALIDimmableLamp(DALIGear, BaseDimmer):
526
+ family = 'dali'
490
527
  manual_add = False
491
528
  name = 'DALI Dimmable Lamp'
492
- discovery_msg = _("Please hook up your new DALI lamp to your DALI bus.")
529
+
493
530
 
simo/fleet/forms.py CHANGED
@@ -6,7 +6,9 @@ from django.contrib.contenttypes.models import ContentType
6
6
  from dal import autocomplete
7
7
  from dal import forward
8
8
  from simo.core.models import Component
9
- from simo.core.forms import BaseComponentForm, ValueLimitForm, NumericSensorForm
9
+ from simo.core.forms import (
10
+ BaseComponentForm, ValueLimitForm, NumericSensorForm, SwitchForm
11
+ )
10
12
  from simo.core.utils.formsets import FormsetField
11
13
  from simo.core.widgets import LogOutputWidget
12
14
  from simo.core.utils.easing import EASING_CHOICES
@@ -64,7 +66,6 @@ class InterfaceAdminForm(forms.ModelForm):
64
66
  model = Interface
65
67
  fields = '__all__'
66
68
 
67
-
68
69
  def clean(self):
69
70
  if self.instance.pk:
70
71
  return self.cleaned_data
@@ -80,26 +81,6 @@ class InterfaceAdminForm(forms.ModelForm):
80
81
  )
81
82
 
82
83
 
83
-
84
- def clean_scl_pin(self):
85
- if self.cleaned_data['scl_pin'].occupied_by \
86
- and self.cleaned_data['scl_pin'].occupied_by != self.instance:
87
- raise forms.ValidationError(
88
- f"This pin is already occupied by "
89
- f"{self.cleaned_data['scl_pin'].occupied_by}!"
90
- )
91
- return self.cleaned_data['scl_pin']
92
-
93
- def clean_sda_pin(self):
94
- if self.cleaned_data['sda_pin'].occupied_by \
95
- and self.cleaned_data['sda_pin'].occupied_by != self.instance:
96
- raise forms.ValidationError(
97
- f"This pin is already occupied by "
98
- f"{self.cleaned_data['sda_pin'].occupied_by}!"
99
- )
100
- return self.cleaned_data['sda_pin']
101
-
102
-
103
84
  class ColonelComponentForm(BaseComponentForm):
104
85
  colonel = forms.ModelChoiceField(
105
86
  label="Colonel", queryset=Colonel.objects.all(),
@@ -1045,3 +1026,16 @@ class DALIDeviceConfigForm(ColonelComponentForm):
1045
1026
  return super().save(commit=commit)
1046
1027
 
1047
1028
 
1029
+ class DaliSwitchForm(DALIDeviceConfigForm, SwitchForm):
1030
+
1031
+ auto_off = forms.FloatField(
1032
+ required=False, min_value=0.01, max_value=1000000000,
1033
+ help_text="If provided, switch will be turned off after "
1034
+ "given amount of seconds after last turn on event."
1035
+ )
1036
+ inverse = forms.BooleanField(
1037
+ label=_("Inverse switch value"), required=False
1038
+ )
1039
+
1040
+
1041
+
simo/fleet/gateways.py CHANGED
@@ -5,7 +5,7 @@ from simo.core.gateways import BaseObjectCommandsGatewayHandler
5
5
  from simo.core.forms import BaseGatewayForm
6
6
  from simo.core.models import Gateway
7
7
  from simo.core.events import GatewayObjectCommand
8
-
8
+ from simo.core.utils.serialization import deserialize_form_data
9
9
 
10
10
 
11
11
  class FleetGatewayHandler(BaseObjectCommandsGatewayHandler):
@@ -15,7 +15,7 @@ class FleetGatewayHandler(BaseObjectCommandsGatewayHandler):
15
15
  periodic_tasks = (
16
16
  ('look_for_updates', 600),
17
17
  ('watch_colonels_connection', 30),
18
- ('push_discoveries', 10),
18
+ ('push_discoveries', 6),
19
19
  )
20
20
 
21
21
  def _on_mqtt_message(self, client, userdata, msg):
@@ -47,7 +47,16 @@ class FleetGatewayHandler(BaseObjectCommandsGatewayHandler):
47
47
  colonel = Colonel.objects.get(
48
48
  id=gw.discovery['init_data']['colonel']['val'][0]['pk']
49
49
  )
50
- print("Publish discover-ttlock command!")
51
- GatewayObjectCommand(
52
- gw, colonel, command='discover-ttlock',
53
- ).publish()
50
+ if gw.discovery['controller_uid'] == 'simo.fleet.controllers.TTLock':
51
+ GatewayObjectCommand(
52
+ gw, colonel, command='discover',
53
+ type=gw.discovery['controller_uid']
54
+ ).publish()
55
+ elif gw.discovery['controller_uid'] == 'simo.fleet.controllers.DALIDevice':
56
+ form_cleaned_data = deserialize_form_data(gw.discovery['init_data'])
57
+ GatewayObjectCommand(
58
+ gw, colonel,
59
+ command=f'discover',
60
+ type=gw.discovery['controller_uid'],
61
+ i=form_cleaned_data['interface'].no
62
+ ).publish()
@@ -7,7 +7,7 @@ def create_objects(apps, schema_editor):
7
7
  Interface = apps.get_model("fleet", "Interface")
8
8
  for i2c_i in I2CInterface.objects.filter(no__gt=0):
9
9
  Interface.objects.create(
10
- colonel=i2c_i.colonel, type='i2c',
10
+ no=i2c_i.no, colonel=i2c_i.colonel, type='i2c',
11
11
  pin_a=i2c_i.scl_pin, pin_b=i2c_i.sda_pin
12
12
  )
13
13
 
simo/fleet/models.py CHANGED
@@ -387,6 +387,7 @@ def post_interface_save(sender, instance, created, *args, **kwargs):
387
387
  )
388
388
  instance.pin_b.occupied_by = instance
389
389
  instance.pin_b.save()
390
+ instance.save()
390
391
 
391
392
 
392
393
  @receiver(post_delete, sender=Interface)
@@ -240,6 +240,8 @@ class FleetConsumer(AsyncWebsocketConsumer):
240
240
  ),
241
241
  'config': comp.controller._get_colonel_config()
242
242
  }
243
+ if hasattr(comp.controller, 'family'):
244
+ comp_config['family'] = comp.controller.family
243
245
  slaves = [
244
246
  s.id for s in comp.slaves.all()
245
247
  if s.config.get('colonel') == self.colonel.id
@@ -313,16 +315,10 @@ class FleetConsumer(AsyncWebsocketConsumer):
313
315
  'command': 'set_config', 'data': config
314
316
  }, compress=True)
315
317
  asyncio.run(send_config())
316
- elif payload.get('command') == 'discover-ttlock':
317
- print("SEND discover-ttlock command!")
318
- asyncio.run(self.send_data({
319
- 'command': 'discover-ttlock'
320
- }))
321
- elif payload.get('command') == 'discover-dali':
322
- print("SEND discover-dali command!")
323
- asyncio.run(self.send_data({
324
- 'command': 'discover-dali', 'i': payload['interface']
325
- }))
318
+ elif payload.get('command') == 'discover':
319
+ print(f"SEND discover command for {payload['type']}")
320
+ asyncio.run(self.send_data(payload))
321
+
326
322
  elif payload.get('command') == 'finalize':
327
323
  asyncio.run(self.send_data({
328
324
  'command': 'finalize',
@@ -409,7 +405,7 @@ class FleetConsumer(AsyncWebsocketConsumer):
409
405
  save_codes, thread_sensitive=True
410
406
  )(data['codes'])
411
407
  if 'fingerprints' in data and component.controller_uid == TTLock.uid:
412
- def save_codes(codes):
408
+ def save_fingerprints(codes):
413
409
  component.meta['fingerprints'] = codes
414
410
  for code in codes:
415
411
  Fingerprint.objects.get_or_create(
@@ -418,7 +414,7 @@ class FleetConsumer(AsyncWebsocketConsumer):
418
414
  )
419
415
  component.save()
420
416
  await sync_to_async(
421
- save_codes, thread_sensitive=True
417
+ save_fingerprints, thread_sensitive=True
422
418
  )(data['fingerprints'])
423
419
 
424
420
  except Exception as e:
simo/fleet/views.py CHANGED
@@ -42,9 +42,9 @@ class PinsSelectAutocomplete(autocomplete.Select2QuerySetView):
42
42
  return qs
43
43
 
44
44
 
45
- class InterfaceSelectAutocomplete(autocomplete.Select2ListView):
45
+ class InterfaceSelectAutocomplete(autocomplete.Select2QuerySetView):
46
46
 
47
- def get_list(self):
47
+ def get_queryset(self):
48
48
  if not self.request.user.is_staff:
49
49
  return Interface.objects.none()
50
50
 
@@ -542,7 +542,7 @@ class Gate(ControllerBase, TimerMixin):
542
542
 
543
543
 
544
544
  class Blinds(ControllerBase, TimerMixin):
545
- name = _("Blinds")
545
+ name = _("Blind")
546
546
  base_type = 'blinds'
547
547
  gateway_class = GenericGatewayHandler
548
548
  config_form = BlindsConfigForm
Binary file
simo/users/api.py CHANGED
@@ -227,11 +227,13 @@ class FingerprintViewSet(
227
227
  basename = 'fingerprints'
228
228
  serializer_class = FingerprintSerializer
229
229
 
230
-
231
230
  def get_queryset(self):
232
- return Fingerprint.objects.filter(
231
+ qs = Fingerprint.objects.filter(
233
232
  Q(user=None) | Q(user__roles__instance=self.instance)
234
233
  )
234
+ if 'values' in self.request.GET:
235
+ qs = qs.filter(value__in=self.request.GET['values'].split(','))
236
+ return qs
235
237
 
236
238
  def check_can_manage_user(self, request):
237
239
  user_role = request.user.get_role(self.instance)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: simo
3
- Version: 2.0.7
3
+ Version: 2.0.9
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
@@ -43,7 +43,7 @@ simo/core/gateways.py,sha256=s_c2W0v2_te89i6LS4Nj7F2wn9UwjZXPT7pfy6SToVo,3714
43
43
  simo/core/loggers.py,sha256=EBdq23gTQScVfQVH-xeP90-wII2DQFDjoROAW6ggUP4,1645
44
44
  simo/core/managers.py,sha256=WoQ4OX3akIvoroSYji-nLVqXBSJzCiC1u_IiWkKbKmA,2413
45
45
  simo/core/middleware.py,sha256=64PYjnyRnYf4sgMvPfR0oQqf9UEtxUwnhJe3RV6z_HI,2040
46
- simo/core/models.py,sha256=VNoHjHUFQRvKH4ejv4kGv5E6a4bSOhuM8sfAlRYaj-g,19036
46
+ simo/core/models.py,sha256=j5AymbJFt5HOIOYsHJ8UUKhb1TvIoqgH0T1y3BeGJuM,19408
47
47
  simo/core/permissions.py,sha256=UmFjGPDWtAUbaWxJsWORb2q6BREHqndv9mkSIpnmdLk,1379
48
48
  simo/core/routing.py,sha256=X1_IHxyA-_Q7hw1udDoviVP4_FSBDl8GYETTC2zWTbY,499
49
49
  simo/core/serializers.py,sha256=miEh2Sd47KmB0viyNlwrEG_1kxYiTUrvQQvisEoLYuA,15759
@@ -63,7 +63,7 @@ simo/core/__pycache__/api_meta.cpython-38.pyc,sha256=bdk1PG7FuRfFwcvQtF103H0kQrx
63
63
  simo/core/__pycache__/app_widgets.cpython-38.pyc,sha256=9Es2wZNduzUJv-jZ_HX0-L3vqwpXWBbseEwoC5K6b-w,3465
64
64
  simo/core/__pycache__/auto_urls.cpython-38.pyc,sha256=SVl4fF0-yiq7e9gt08jIM6_rL4JYcR0cNHzR9jCEi1M,931
65
65
  simo/core/__pycache__/autocomplete_views.cpython-38.pyc,sha256=hJ6JILI1LqrAtpQMvxnLvljGdW1v1gpvBsD79vFkZ58,3972
66
- simo/core/__pycache__/base_types.cpython-38.pyc,sha256=Ukc5U63YJbyUcn0cwNMS8nteRSvEJmCszZUfHClKBtg,735
66
+ simo/core/__pycache__/base_types.cpython-38.pyc,sha256=CasZJN42cK_ymoQgn5E4s8oOkuZJ18fVHCgN4GPuT7c,735
67
67
  simo/core/__pycache__/context.cpython-38.pyc,sha256=MSZPDhqMhCpUuBJl3HCIBHZA3BntYeP8RAnQcdqAH9k,1278
68
68
  simo/core/__pycache__/controllers.cpython-38.pyc,sha256=-NjuX7iGheE_ZMqkZ6g4ZnnVjdUnFCTlH0awAJxZt2Y,24110
69
69
  simo/core/__pycache__/dynamic_settings.cpython-38.pyc,sha256=ELu06Hub4DOidja71ybvD3ZM4HdXiyZjNJrZfnXZXNA,2476
@@ -74,7 +74,7 @@ simo/core/__pycache__/gateways.cpython-38.pyc,sha256=XBiwMfBkjoQ2re6jvADJOwK0_0A
74
74
  simo/core/__pycache__/loggers.cpython-38.pyc,sha256=Z-cdQnC6XlIonPV4Sl4E52tP4NMEdPAiHK0cFaIL7I8,1623
75
75
  simo/core/__pycache__/managers.cpython-38.pyc,sha256=5vstOMfm997CZBBkaSiaS7EojhLTWZlbeA_EQ8u-yfg,2554
76
76
  simo/core/__pycache__/middleware.cpython-38.pyc,sha256=bGOFJNEhJeLbpsZp8LYn1VA3paLF5HULHQ6IFKa7Juc,2022
77
- simo/core/__pycache__/models.cpython-38.pyc,sha256=Hl9MXjnnmDTp826aEc5KLqPFl4ZzrT7HTVxVSz2-Vik,16695
77
+ simo/core/__pycache__/models.cpython-38.pyc,sha256=Gm36LWRxswvWiB3Wz0F7g32ZVXugh7chSSBz1lgBPZs,16995
78
78
  simo/core/__pycache__/permissions.cpython-38.pyc,sha256=uygjPbfRQiEzyo5-McCxsuMDJLbDYO_TLu55U7bJbR0,1809
79
79
  simo/core/__pycache__/routing.cpython-38.pyc,sha256=3T3FPJ8Cn99xZCGvMyg2xjl7al-Shm9CelbSpkJtNP8,599
80
80
  simo/core/__pycache__/serializers.cpython-38.pyc,sha256=tJf8jV9pPJXnJjUPXCc103wQxC5ZrZaC9SlIR41JomQ,15707
@@ -10165,32 +10165,34 @@ simo/fleet/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10165
10165
  simo/fleet/admin.py,sha256=aQulOPtrIQUKj8WvA4GfINfqVA52fcU72yK2hVlcmJ0,5681
10166
10166
  simo/fleet/api.py,sha256=Hxn84xI-Q77HxjINgRbjSJQOv9jii4OL20LxK0VSrS8,2499
10167
10167
  simo/fleet/auto_urls.py,sha256=X04oKJWA48wFW5iXg3PPROY2KDdHn_a99orQSE28QC4,518
10168
+ simo/fleet/base_types.py,sha256=wL9RVkHr0gA7HI1wZq0pruGEIgvQqpfnCL4cC3ywsvw,102
10168
10169
  simo/fleet/ble.py,sha256=eHA_9ABjbmH1vUVCv9hiPXQL2GZZSEVwfO0xyI1S0nI,1081
10169
- simo/fleet/controllers.py,sha256=VxutXZSZaZHmXGYpqjsC_6DgJsF1ZDZeJwu3px79wSE,16683
10170
- simo/fleet/forms.py,sha256=pR_KER9u9VtNb5NpSWwI7LrnBBtn9jbEXzUvyFRzoj0,35986
10171
- simo/fleet/gateways.py,sha256=m-jF8S_kJCI-yLI0UmWm46xZrsHcvSfw2gozZWRoFfE,1707
10170
+ simo/fleet/controllers.py,sha256=LF5-Mst617BuZHyRErk_6diAk85pa5uelGoTelBY184,17743
10171
+ simo/fleet/forms.py,sha256=lWJ9luZiRJD4yLQCdCMmkvezk-vY3yVb3q7Y5C4r9qc,35649
10172
+ simo/fleet/gateways.py,sha256=EbSxv1DGSuv8MT7MmpZxKgV041umOteFW2Xo4LybzWI,2284
10172
10173
  simo/fleet/managers.py,sha256=XOpDOA9L-f_550TNSyXnJbun2EmtGz1TenVTMlUSb8E,807
10173
- simo/fleet/models.py,sha256=7ZZ1z_cId7z9bEzLHvEEDtpFwUMgIUDesr5hhveDcWg,14136
10174
+ simo/fleet/models.py,sha256=J-rnn7Ew-7s3646NNRVY947Sbz21mUD_nBHtuHAKXds,14160
10174
10175
  simo/fleet/routing.py,sha256=cofGsVWXMfPDwsJ6HM88xxtRxHwERhJ48Xyxc8mxg5o,149
10175
10176
  simo/fleet/serializers.py,sha256=zEpXAXxjk4Rf1JhlNnLTrs20qJggqjvIySbeHVo4Tt4,1505
10176
- simo/fleet/socket_consumers.py,sha256=ii5L1di8iXzn-l91zVFQ_g9YQm_a-_Z-Dn5OnKbJ9FI,19876
10177
+ simo/fleet/socket_consumers.py,sha256=e_PtRWLPIrJxNBhtQIAC7idk1oaV-LVZWQchz5iffUk,19680
10177
10178
  simo/fleet/utils.py,sha256=2gcjbwQawsGw2edr_wm9q6XacGpYqO-gd4BF1t0Hg6U,3511
10178
- simo/fleet/views.py,sha256=WMMG8rEJA6RTOf3U-2sYbiSH2Q3rBC-6kYg9KpYHtlI,1686
10179
+ simo/fleet/views.py,sha256=YKkcf8KcLgiPjr-brIHvu5yr1zZUIs8aytAgwdo49Pg,1694
10179
10180
  simo/fleet/__pycache__/__init__.cpython-38.pyc,sha256=pIZE7EL6-cuJ3pQtaSwjKLrKLsTYelp1k9sRhXKLh6s,159
10180
10181
  simo/fleet/__pycache__/admin.cpython-38.pyc,sha256=Tw3g9JKzJ3ICjWnHQedU8qV7RSZeJL81jSYpvTLK5bM,5798
10181
10182
  simo/fleet/__pycache__/api.cpython-38.pyc,sha256=rL9fb7cCQatyFvXyKmlNOKmxVo8vHYeYhfSiAgchyAs,3110
10182
10183
  simo/fleet/__pycache__/auto_urls.cpython-38.pyc,sha256=SqyTuaz_kEBvx-bL46SclsZEEP5RFh6U6TGKyXDdiOE,565
10184
+ simo/fleet/__pycache__/base_types.cpython-38.pyc,sha256=deyPwjpT6xZiFxBGFnj5b7R-lbdOTh2krgpJhrcGVhc,274
10183
10185
  simo/fleet/__pycache__/ble.cpython-38.pyc,sha256=Nrof9w7cm4OlpFWHeVnmvvanh2_oF9oQ3TknJiV93-0,1267
10184
- simo/fleet/__pycache__/controllers.cpython-38.pyc,sha256=sOxMEUCc347vDlsC2Pp9h5jxyhax2dFBAVEgP_aTN3s,14848
10185
- simo/fleet/__pycache__/forms.cpython-38.pyc,sha256=7AjtKMSUAuaxLIRx1lvEfRaAlujKRyIMK7gnJjru-fI,25650
10186
- simo/fleet/__pycache__/gateways.cpython-38.pyc,sha256=Qpfw3NYxCS9tS63FaZz8fdL9fq5X1k5QcQlaJl-szb4,2152
10186
+ simo/fleet/__pycache__/controllers.cpython-38.pyc,sha256=2XWWfU0zkbha_MMPD0KbSOnh9CHUNLcsI-VKmr8Z7e0,15559
10187
+ simo/fleet/__pycache__/forms.cpython-38.pyc,sha256=pHgx8ulx_2v0L0bkUHaW13Tn9KcfOP6ROhgHXPVvKJE,25510
10188
+ simo/fleet/__pycache__/gateways.cpython-38.pyc,sha256=xg7fW5JusYwl6epn5nEjxQJlFOyQ18SVybOaRBcpwl8,2418
10187
10189
  simo/fleet/__pycache__/managers.cpython-38.pyc,sha256=8uz-xpUiqbGDgXIZ_XRZtFb-Tju6NGxflGg-Ee4Yo6k,1310
10188
- simo/fleet/__pycache__/models.cpython-38.pyc,sha256=1nBzbxLzIOYxss3wx0-lyGP-JCHnGdbADH_NDQ4If50,12273
10190
+ simo/fleet/__pycache__/models.cpython-38.pyc,sha256=S9pPqRjIxASXahoIOkkjQX7cBwjkdu4d2nXMju0-Cf8,12283
10189
10191
  simo/fleet/__pycache__/routing.cpython-38.pyc,sha256=aPrCmxFKVyB8R8ZbJDwdPdFfvT7CvobovvZeq_mqRgY,314
10190
10192
  simo/fleet/__pycache__/serializers.cpython-38.pyc,sha256=yuY2H7jcboQGZdjb5WIsgNHXFhI9IPMUrEu9NgSiXNo,2452
10191
- simo/fleet/__pycache__/socket_consumers.cpython-38.pyc,sha256=5bmMyD3gRyfR2Hbh0KwZwMPYzZR8NPWuMhN8PHWOqLg,14408
10193
+ simo/fleet/__pycache__/socket_consumers.cpython-38.pyc,sha256=dAq59u6jcxHZ3W9dR0HSp_kfiZUhMPVja0SqJ38crhk,14399
10192
10194
  simo/fleet/__pycache__/utils.cpython-38.pyc,sha256=dTuvW9MnhUycwdCc6eHYfHsMlvZw-CmEWXWYu18X8Uw,1955
10193
- simo/fleet/__pycache__/views.cpython-38.pyc,sha256=HLHXI3JgCDUWP7oY944nBqpVVu0D6-qOuiSMraNUdq8,1825
10195
+ simo/fleet/__pycache__/views.cpython-38.pyc,sha256=7hhRBlf6Vczg0TTdwTE5Hc5B-F7VcvFg1iS6mfs-fgo,1790
10194
10196
  simo/fleet/migrations/0001_initial.py,sha256=lce8nkD8Sz6pYr-XJSpDm4CMDuB6TA__WtnHpIp-eA4,1326
10195
10197
  simo/fleet/migrations/0002_auto_20220422_0743.py,sha256=sFOfAjnQOzcJjE8lHrrHgTaGilJNYswMdXphgVzUZqY,825
10196
10198
  simo/fleet/migrations/0003_auto_20220422_0752.py,sha256=VcH7DyMAniEwT76hDVofS8FTNpM3nxz_J9AC2zKHDSA,543
@@ -10223,7 +10225,7 @@ simo/fleet/migrations/0029_alter_i2cinterface_scl_pin_and_more.py,sha256=aquymc1
10223
10225
  simo/fleet/migrations/0030_colonelpin_label_alter_colonel_type.py,sha256=5T5bmQxPZrG0UseCLd7ssV-AeFF3O4T_DFxLu3whSqg,706
10224
10226
  simo/fleet/migrations/0031_alter_colonel_type.py,sha256=Kv_Gld-X0NZ7d2P9WBXfIlhq4iPH1_NnzDrPyy9_Pos,522
10225
10227
  simo/fleet/migrations/0032_auto_20240415_0736.py,sha256=yBT48pIfDBm7J1Y3LrvNtmihRbwEXjmBGBB1ghc2z5Y,1800
10226
- simo/fleet/migrations/0033_auto_20240415_0736.py,sha256=_7p3-Qdc7aaP4mDwny_ig5E4XoEm8t2PoZ3-UeQ4jio,758
10228
+ simo/fleet/migrations/0033_auto_20240415_0736.py,sha256=3ysmu-fkzwY_7l4tDIHEylcoqXGdW_KMKHs1J3_0i5w,771
10227
10229
  simo/fleet/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10228
10230
  simo/fleet/migrations/__pycache__/0001_initial.cpython-38.pyc,sha256=9kc1UyMEYkRNVnZ7iwZbiW1t3qWXROvWrI2G1BdzIaA,1250
10229
10231
  simo/fleet/migrations/__pycache__/0002_auto_20220422_0743.cpython-38.pyc,sha256=8oxhGb7rL8QYKlBLU3pOYcd8aHeQWDB9I8awkK04mXg,841
@@ -10257,12 +10259,12 @@ simo/fleet/migrations/__pycache__/0029_alter_i2cinterface_scl_pin_and_more.cpyth
10257
10259
  simo/fleet/migrations/__pycache__/0030_colonelpin_label_alter_colonel_type.cpython-38.pyc,sha256=sfglSDxXLKJ0qE8Dl3MYjv5hbszpDtb9CDxatoEzPSw,853
10258
10260
  simo/fleet/migrations/__pycache__/0031_alter_colonel_type.cpython-38.pyc,sha256=zXX254ZgEE4uSV8xnLdH9DM3qy-ICmbrT05i0Q287bU,733
10259
10261
  simo/fleet/migrations/__pycache__/0032_auto_20240415_0736.cpython-38.pyc,sha256=QD3JNIDQhzseXKLRYysYY3Q9_vDaurIhlWBcri83FMw,1655
10260
- simo/fleet/migrations/__pycache__/0033_auto_20240415_0736.cpython-38.pyc,sha256=XBy2jZZ4kDRn06KC9YcHRnoaol8heCMAvHZ3BAELr2c,1031
10262
+ simo/fleet/migrations/__pycache__/0033_auto_20240415_0736.cpython-38.pyc,sha256=zN3KrYHzE40Wfnt48zp7AGCIa8Jj9-fPXxo3adZIxwo,1046
10261
10263
  simo/fleet/migrations/__pycache__/__init__.cpython-38.pyc,sha256=5k1KW0jeSDzw6RnVPRq4CaO13Lg7M0F-pxA_gqqZ6Mg,170
10262
10264
  simo/generic/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10263
10265
  simo/generic/app_widgets.py,sha256=E_pnpA1hxMIhenRCrHoQ5cik06jm2BAHCkl_eo-OudU,1264
10264
10266
  simo/generic/base_types.py,sha256=djymox_boXTHX1BTTCLXrCH7ED-uAsV_idhaDOc3OLI,409
10265
- simo/generic/controllers.py,sha256=hsjgCSpePeOndZP8PU5SoLid6URbJ5a5Yte2wbeRcT0,51676
10267
+ simo/generic/controllers.py,sha256=Mn4jmOx-1Yb5rAZb9ZcfMcYBMf7r61NQV2p8JgxbYvQ,51675
10266
10268
  simo/generic/forms.py,sha256=p-gzH1z7J0CV8bKMmWTQluzpYmgc7fF1aEHL7eL-fJI,20075
10267
10269
  simo/generic/gateways.py,sha256=BEHjNZ2VpblXxpuADZzRs4IAO7fsj2Heb2peGXFXNec,15156
10268
10270
  simo/generic/models.py,sha256=d00Q-UXtt7mG9MdPpZ3mXnxKjwlTI2FgCEklZpGBk7s,3629
@@ -10271,7 +10273,7 @@ simo/generic/socket_consumers.py,sha256=NfTQGYtVAc864IoogZRxf_0xpDPM0eMCWn0SlKA5
10271
10273
  simo/generic/__pycache__/__init__.cpython-38.pyc,sha256=mLu54WS9KIl-pHwVCBKpsDFIlOqml--JsOVzAUHg6cU,161
10272
10274
  simo/generic/__pycache__/app_widgets.cpython-38.pyc,sha256=0IoKRG9n1tkNRRkrqAeOQwWBPd_33u98JBcVtMVVCio,2374
10273
10275
  simo/generic/__pycache__/base_types.cpython-38.pyc,sha256=ptw6axyAqemZA35oa6vzr7EihzvbhW9w7Y-G6kfDedU,555
10274
- simo/generic/__pycache__/controllers.cpython-38.pyc,sha256=Qk5jyZd5-xs746pnJTTbT5g4kpZZOZ0FdkbtoRRmad4,32788
10276
+ simo/generic/__pycache__/controllers.cpython-38.pyc,sha256=3U3W-EgpKdqh7MN94ZaoCEGvY4SSq2zsTptJoZbysgM,32795
10275
10277
  simo/generic/__pycache__/forms.cpython-38.pyc,sha256=Uvh-YQxcN-0RCinRi13fyITM1Jxm6FwYQyXs3OLNruA,15699
10276
10278
  simo/generic/__pycache__/gateways.cpython-38.pyc,sha256=43zzltUplgyKIBN9Knk-YNstFgm2Mjk2CvW6vwuzeio,11582
10277
10279
  simo/generic/__pycache__/models.cpython-38.pyc,sha256=ItjBjJaioJttzSUJsHCKV4xiqH6QX90kRw1C_Xx7mMk,3065
@@ -10349,7 +10351,7 @@ simo/notifications/migrations/__pycache__/0002_notification_instance.cpython-38.
10349
10351
  simo/notifications/migrations/__pycache__/__init__.cpython-38.pyc,sha256=YMBRHVon2nWDtIUbghckjnC12sIg_ykPWhV5aM0tto4,178
10350
10352
  simo/users/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10351
10353
  simo/users/admin.py,sha256=IY501Ewbn8KkBJ3ryBPelHU37ac2--dd6NNGsmYQ6FE,6887
10352
- simo/users/api.py,sha256=xJCCj75eQXdaYfiCxxG_rMDKzEZuYuCadOhOutqX2YY,9053
10354
+ simo/users/api.py,sha256=w5nmU-to1-ULnPcRKDpa6uaOUwWfbHMTQKfmSJx3qPg,9185
10353
10355
  simo/users/auth_backends.py,sha256=I5pnaTa20-Lxfw_dFG8471xDITb0_fQl1PVhJalp5vU,3992
10354
10356
  simo/users/auto_urls.py,sha256=lcJvteBsbHQMJieZpDz-63tDYejLApqsW3CUnDakd7k,272
10355
10357
  simo/users/dynamic_settings.py,sha256=sEIsi4yJw3kH46Jq_aOkSuK7QTfQACGUE-lkyBogCaM,570
@@ -10364,7 +10366,7 @@ simo/users/utils.py,sha256=uOyCNHQJ_tpn054L89keuQwKAzFd0Y2VSUmDVPq6oaY,1335
10364
10366
  simo/users/views.py,sha256=dOQVvmlHG7ihWKJLFUBcqKOA0UDctlMKR0pTc36JZqg,3487
10365
10367
  simo/users/__pycache__/__init__.cpython-38.pyc,sha256=9otuYxq331c4lGy0DR8pigaPpzq0lQ4nrNLhlYiFAF0,159
10366
10368
  simo/users/__pycache__/admin.cpython-38.pyc,sha256=T7gzc4xv2JIh3nkJEr4aRZAzyaaweX4m9JXJ-xzwsd0,7705
10367
- simo/users/__pycache__/api.cpython-38.pyc,sha256=PFpZ14CdzCg4nw8dPeWuF7nKnJSH8ipQT_efM-KiFTQ,7889
10369
+ simo/users/__pycache__/api.cpython-38.pyc,sha256=rFjlT4o4Ossop70GlljQPTm1-_TH-omnr8aDvOnMgH8,7981
10368
10370
  simo/users/__pycache__/auth_backends.cpython-38.pyc,sha256=MuOieBIXt6lrDx83-UQtdDyI_U8kE3pU9XR4yFLKBnE,3007
10369
10371
  simo/users/__pycache__/auto_urls.cpython-38.pyc,sha256=K-3sz2h-cEitoflSmZk1t0eUg5mQMMGLNZFREVwG7_o,430
10370
10372
  simo/users/__pycache__/dynamic_settings.cpython-38.pyc,sha256=6F8JBjZkHykySnmZjNEzjS0ijbmPdcp9yUAZ5kqq_Fo,864
@@ -10437,8 +10439,8 @@ simo/users/templates/invitations/expired_msg.html,sha256=47DEQpj8HBSa-_TImW-5JCe
10437
10439
  simo/users/templates/invitations/expired_suggestion.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10438
10440
  simo/users/templates/invitations/taken_msg.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10439
10441
  simo/users/templates/invitations/taken_suggestion.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10440
- simo-2.0.7.dist-info/LICENSE.md,sha256=M7wm1EmMGDtwPRdg7kW4d00h1uAXjKOT3HFScYQMeiE,34916
10441
- simo-2.0.7.dist-info/METADATA,sha256=D3DwzzxRfVOdr1NCw4Me2MvxkgLnZ-jPQdbnREr0oV0,1699
10442
- simo-2.0.7.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
10443
- simo-2.0.7.dist-info/top_level.txt,sha256=GmS1hrAbpVqn9OWZh6UX82eIOdRLgYA82RG9fe8v4Rs,5
10444
- simo-2.0.7.dist-info/RECORD,,
10442
+ simo-2.0.9.dist-info/LICENSE.md,sha256=M7wm1EmMGDtwPRdg7kW4d00h1uAXjKOT3HFScYQMeiE,34916
10443
+ simo-2.0.9.dist-info/METADATA,sha256=IBLtc0JzeJ7o-RPgd4LblELV30WxBumyHeejuRcT7EE,1699
10444
+ simo-2.0.9.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
10445
+ simo-2.0.9.dist-info/top_level.txt,sha256=GmS1hrAbpVqn9OWZh6UX82eIOdRLgYA82RG9fe8v4Rs,5
10446
+ simo-2.0.9.dist-info/RECORD,,
File without changes