simo 2.5.40__py3-none-any.whl → 2.5.42__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
Binary file
simo/core/api.py CHANGED
@@ -12,6 +12,7 @@ from actstream.models import Action
12
12
  from rest_framework.pagination import PageNumberPagination
13
13
  from rest_framework import viewsets
14
14
  from django_filters.rest_framework import DjangoFilterBackend
15
+ from django.core.cache import cache
15
16
  from rest_framework.decorators import action
16
17
  from rest_framework.response import Response as RESTResponse
17
18
  from rest_framework.exceptions import ValidationError as APIValidationError
@@ -143,43 +144,55 @@ class ZoneViewSet(InstanceMixin, viewsets.ModelViewSet):
143
144
  return RESTResponse({'status': 'success'})
144
145
 
145
146
 
146
- def get_components_queryset(instance, user):
147
- qs = Component.objects.filter(zone__instance=instance)
148
- if user.is_superuser:
149
- return qs
150
-
151
- c_ids = set()
147
+ def get_main_components_ids(instance):
148
+ cache_key = f"main-components-{instance.id}"
149
+ ids = cache.get(cache_key)
150
+ if ids:
151
+ return ids
152
152
 
153
+ ids = []
153
154
  from simo.generic.controllers import Weather
154
155
 
155
156
  if instance.indoor_climate_sensor:
156
- c_ids.add(instance.indoor_climate_sensor.id)
157
+ ids.append(instance.indoor_climate_sensor.id)
157
158
  wf_c = Component.objects.filter(
158
159
  zone__instance=instance,
159
160
  controller_uid=Weather.uid, config__is_main=True
160
161
  ).values('id').first()
161
162
  if wf_c:
162
- c_ids.add(wf_c['id'])
163
+ ids.append(wf_c['id'])
163
164
  main_alarm_group = Component.objects.filter(
164
165
  zone__instance=instance,
165
166
  base_type='alarm-group', config__is_main=True
166
167
  ).values('id').first()
167
168
  if main_alarm_group:
168
- c_ids.add(main_alarm_group['id'])
169
+ ids.append(main_alarm_group['id'])
169
170
  state = Component.objects.filter(
170
171
  zone__instance=instance,
171
172
  base_type='state-select', config__is_main=True
172
173
  ).values('id').first()
173
174
  if state:
174
- c_ids.add(state['id'])
175
+ ids.append(state['id'])
176
+
177
+ cache.set(cache_key, ids, 30)
178
+ return ids
175
179
 
176
- user_role = user.get_role(instance)
177
- if user_role:
178
- for cp in user_role.component_permissions.all():
179
- if cp.read:
180
- c_ids.add(cp.component.id)
181
180
 
182
- return qs.filter(id__in=c_ids).select_related(
181
+ def get_components_queryset(instance, user):
182
+ qs = Component.objects.filter(zone__instance=instance)
183
+ if user.is_master or user.is_superuser:
184
+ pass
185
+ else:
186
+ c_ids = get_main_components_ids(instance)
187
+
188
+ user_role = user.get_role(instance)
189
+ if user_role:
190
+ for cp in user_role.component_permissions.all():
191
+ if cp.read:
192
+ c_ids.append(cp.component.id)
193
+ qs = qs.filter(id__in=c_ids)
194
+
195
+ return qs.select_related(
183
196
  'zone', 'category', 'icon'
184
197
  ).prefetch_related('slaves')
185
198
 
@@ -198,7 +211,14 @@ class ComponentViewSet(
198
211
  return permissions
199
212
 
200
213
  def get_queryset(self):
201
- return get_components_queryset(self.instance, self.request.user)
214
+ qs = get_components_queryset(self.instance, self.request.user)
215
+ if self.request.GET.get('id'):
216
+ try:
217
+ ids = [int(id) for id in self.request.GET.get('id').split(',')]
218
+ return qs.filter(id__in=ids)
219
+ except:
220
+ return qs
221
+ return qs
202
222
 
203
223
  def get_view_name(self):
204
224
  singular = "Component"
@@ -50,7 +50,6 @@ killasgroup=true
50
50
  command={{ venv_path }}/python {{ project_dir }}/hub/manage.py gateways_manager
51
51
  process_name=%(program_name)s
52
52
  user=root
53
- stopsignal=INT
54
53
  stopwaitsecs=10
55
54
  stdout_logfile=/var/log/simo/gateways.log
56
55
  stdout_logfile_maxbytes=1MB
simo/core/permissions.py CHANGED
@@ -80,7 +80,7 @@ class ComponentPermission(BasePermission):
80
80
  return True
81
81
  if request.user.is_master:
82
82
  return True
83
- if obj.controller.masters_only:
83
+ if obj.controller and obj.controller.masters_only:
84
84
  return False
85
85
  user_role = request.user.get_role(view.instance)
86
86
  if user_role.is_superuser:
simo/core/serializers.py CHANGED
@@ -544,7 +544,7 @@ class ComponentSerializer(FormSerializer):
544
544
  return {'type': app_widget.uid, 'size': app_widget.size}
545
545
 
546
546
  def get_slaves(self, obj):
547
- return [c['id'] for c in obj.slaves.all().values('id')]
547
+ return [s.id for s in obj.slaves.all()]
548
548
 
549
549
  def get_masters_only(self, obj):
550
550
  if not obj.controller:
Binary file
simo/fleet/controllers.py CHANGED
@@ -661,6 +661,7 @@ class TTLock(FleeDeviceMixin, Lock):
661
661
  self.component.save(update_fields=['meta'])
662
662
 
663
663
 
664
+
664
665
  class DALIDevice(FleeDeviceMixin, ControllerBase):
665
666
  gateway_class = FleetGatewayHandler
666
667
  config_form = DALIDeviceConfigForm
simo/fleet/forms.py CHANGED
@@ -1459,6 +1459,18 @@ class TTLockConfigForm(ColonelComponentForm):
1459
1459
  ), required=False,
1460
1460
  help_text="Quickens up lock status reporting on open/close if provided."
1461
1461
  )
1462
+ auto_lock = forms.IntegerField(
1463
+ min_value=0, max_value=60, required=False,
1464
+ help_text="Lock the lock after given amount of seconds."
1465
+ )
1466
+ inverse = forms.BooleanField(
1467
+ required=False,
1468
+ help_text="Inverse operation (if supported by the lock)."
1469
+ )
1470
+
1471
+ def __init__(self, *args, **kwargs):
1472
+ super().__init__(*args, **kwargs)
1473
+ self.basic_fields.extend(['auto_lock', 'inverse'])
1462
1474
 
1463
1475
  def clean(self):
1464
1476
  if not self.instance or not self.instance.pk:
@@ -1475,10 +1487,17 @@ class TTLockConfigForm(ColonelComponentForm):
1475
1487
 
1476
1488
  def save(self, commit=True):
1477
1489
  obj = super(ColonelComponentForm, self).save(commit)
1478
- if commit and 'door_sensor' in self.cleaned_data:
1490
+ if commit:
1491
+ if 'door_sensor' in self.cleaned_data:
1492
+ GatewayObjectCommand(
1493
+ self.instance.gateway, self.cleaned_data['door_sensor'],
1494
+ command='watch_lock_sensor'
1495
+ ).publish()
1479
1496
  GatewayObjectCommand(
1480
- self.instance.gateway, self.cleaned_data['door_sensor'],
1481
- command='watch_lock_sensor'
1497
+ obj.gateway, self.cleaned_data['colonel'], id=obj.id,
1498
+ command='call', method='update_config', args=[
1499
+ obj.controller._get_colonel_config()
1500
+ ]
1482
1501
  ).publish()
1483
1502
  return obj
1484
1503
 
@@ -169,12 +169,13 @@ class PresenceLighting(Script):
169
169
  sensor.on_change(self._on_sensor)
170
170
  self.sensors[id] = sensor
171
171
 
172
- if self.component.config['off_value'] != 0:
173
- for id in self.component.config['lights']:
174
- light = Component.objects.filter(id=id).first()
175
- if not light or not light.controller:
176
- continue
177
- light.on_change(self._on_light_change)
172
+ for light_params in self.component.config['lights']:
173
+ light = Component.objects.filter(
174
+ id=light_params.get('light')
175
+ ).first()
176
+ if not light or not light.controller:
177
+ continue
178
+ light.on_change(self._on_light_change)
178
179
 
179
180
  for condition in self.component.config.get('conditions', []):
180
181
  comp = Component.objects.filter(
@@ -202,12 +203,10 @@ class PresenceLighting(Script):
202
203
  condition['component'] = condition_comp
203
204
  self._regulate()
204
205
 
205
-
206
206
  def _on_light_change(self, light):
207
207
  if self.is_on:
208
208
  self.light_org_values[light.id] = light.value
209
209
 
210
-
211
210
  def _regulate(self, on_val_change=True):
212
211
  presence_values = [s.value for id, s in self.sensors.items()]
213
212
  if self.component.config.get('act_on', 0) == 0:
@@ -250,12 +249,15 @@ class PresenceLighting(Script):
250
249
  print("Turn the lights ON!")
251
250
  self.is_on = True
252
251
  self.light_org_values = {}
253
- for id in self.component.config['lights']:
254
- comp = Component.objects.filter(id=id).first()
252
+ for light_params in self.component.config['lights']:
253
+ comp = Component.objects.filter(
254
+ id=light_params.get('light')
255
+ ).first()
255
256
  if not comp or not comp.controller:
256
257
  continue
257
258
  self.light_org_values[comp.id] = comp.value
258
- comp.controller.send(self.component.config['on_value'])
259
+ print(f"Send {light_params['on_value']} to {comp}!")
260
+ comp.controller.send(light_params['on_value'])
259
261
  return
260
262
 
261
263
  if self.is_on:
@@ -278,14 +280,14 @@ class PresenceLighting(Script):
278
280
  print("Turn the lights OFF!")
279
281
  self.is_on = False
280
282
  self.last_presence = 0
281
- for id in self.component.config['lights']:
282
- comp = Component.objects.filter(id=id).first()
283
+ for light_params in self.component.config['lights']:
284
+ comp = Component.objects.filter(
285
+ id=light_params.get('light')
286
+ ).first()
283
287
  if not comp or not comp.controller:
284
288
  continue
285
- if self.component.config['off_value'] == 0:
286
- comp.send(0)
287
- else:
288
- comp.send(self.light_org_values.get(comp.id, 0))
289
+ print(f"Send {light_params['on_value']} to {comp}!")
290
+ comp.send(light_params.get('off_value', 0))
289
291
 
290
292
 
291
293
  # TODO: Night lighting
simo/generic/forms.py CHANGED
@@ -200,8 +200,8 @@ class ConditionForm(forms.Form):
200
200
  return self.cleaned_data
201
201
 
202
202
 
203
- class PresenceLightingConfigForm(BaseComponentForm):
204
- lights = Select2ModelMultipleChoiceField(
203
+ class LightTurnOnForm(forms.Form):
204
+ light = Select2ModelChoiceField(
205
205
  queryset=Component.objects.filter(
206
206
  base_type__in=('switch', 'dimmer', 'rgbw-light', 'rgb-light')
207
207
  ),
@@ -212,17 +212,19 @@ class PresenceLightingConfigForm(BaseComponentForm):
212
212
  'base_type'),
213
213
  )
214
214
  )
215
-
216
215
  on_value = forms.IntegerField(
217
216
  min_value=0, initial=100,
218
217
  help_text="Value applicable for dimmers. "
219
- "Switches will receive tunrn on command."
218
+ "Switches will receive turn on command."
220
219
  )
221
220
  off_value = forms.TypedChoiceField(
222
221
  coerce=int, initial=1, choices=(
223
- (0, "0"), (1, "Original value before turning the lights on.")
222
+ (0, "0"), (1, "Original value before turning the light on.")
224
223
  )
225
224
  )
225
+
226
+
227
+ class PresenceLightingConfigForm(BaseComponentForm):
226
228
  presence_sensors = Select2ModelMultipleChoiceField(
227
229
  queryset=Component.objects.filter(
228
230
  base_type__in=('binary-sensor', 'switch')
@@ -242,7 +244,8 @@ class PresenceLightingConfigForm(BaseComponentForm):
242
244
  (0, '----'),
243
245
  (1, "10 s"), (2, "20 s"), (3, "30 s"), (4, "40 s"), (5, "50 s"),
244
246
  (6, "1 min"), (9, "1.5 min"), (12, "2 min"), (18, "3 min"),
245
- (30, "5 min"), (60, "10 min"), (120, "20 min"),
247
+ (30, "5 min"), (60, "10 min"), (120, "20 min"), (180, "30 min"),
248
+ (3600, "1 h")
246
249
  ),
247
250
  help_text="Hold off time after last presence detector is deactivated."
248
251
  )
@@ -251,6 +254,14 @@ class PresenceLightingConfigForm(BaseComponentForm):
251
254
  ConditionForm, can_delete=True, can_order=True, extra=0
252
255
  ), label='Additional conditions'
253
256
  )
257
+
258
+ lights = FormsetField(
259
+ formset_factory(
260
+ LightTurnOnForm, can_delete=True, can_order=True, extra=0
261
+ ), label='Lights'
262
+ )
263
+
264
+
254
265
  autostart = forms.BooleanField(
255
266
  initial=True, required=False,
256
267
  help_text="Start automatically on system boot."
@@ -0,0 +1,34 @@
1
+ # Generated by Django 4.2.10 on 2024-11-26 07:26
2
+ from django.db import migrations
3
+
4
+
5
+ def forwards_func(apps, schema_editor):
6
+ Component = apps.get_model("core", "Component")
7
+
8
+ for script in Component.objects.filter(
9
+ controller_uid='simo.generic.controllers.PresenceLighting'
10
+ ):
11
+ lights = []
12
+ on_value = script.config.pop('on_value', 100)
13
+ off_value = script.config.get('off_value', 0)
14
+ for light_id in script.config.get('lights', []):
15
+ lights.append({
16
+ 'light': light_id, 'on_value': on_value, 'off_value': off_value
17
+ })
18
+ script.config['lights'] = lights
19
+ script.save()
20
+
21
+
22
+ def reverse_func(apps, schema_editor):
23
+ pass
24
+
25
+
26
+ class Migration(migrations.Migration):
27
+
28
+ dependencies = [
29
+ ('generic', '0001_initial'),
30
+ ]
31
+
32
+ operations = [
33
+ migrations.RunPython(forwards_func, reverse_func, elidable=True),
34
+ ]
simo/notifications/api.py CHANGED
@@ -34,7 +34,7 @@ class NotificationsViewSet(
34
34
  archived = bool(int(self.request.query_params['archived']))
35
35
  except:
36
36
  archived = False
37
- qs = Notification.objects.filter(
37
+ qs = qs.objects.filter(
38
38
  user_notifications__archived__isnull=not archived,
39
39
  user_notifications__user=self.request.user
40
40
  )
simo/users/models.py CHANGED
@@ -193,11 +193,19 @@ class User(AbstractBaseUser, SimoAdminMixin):
193
193
  USERNAME_FIELD = 'email'
194
194
  REQUIRED_FIELDS = ['name']
195
195
 
196
+
197
+
196
198
  class Meta:
197
199
  verbose_name = _('user')
198
200
  verbose_name_plural = _('users')
199
201
  abstract = False
200
202
 
203
+ def __init__(self, *args, **kwargs):
204
+ super().__init__(*args, **kwargs)
205
+ self._is_active = None
206
+ self._instances = None
207
+ self._instance_roles = {}
208
+
201
209
  def __str__(self):
202
210
  return self.name
203
211
 
@@ -240,6 +248,8 @@ class User(AbstractBaseUser, SimoAdminMixin):
240
248
  return self.is_active and self.is_master
241
249
 
242
250
  def get_role(self, instance):
251
+ if instance.id in self._instance_roles:
252
+ return self._instance_roles[instance.id]
243
253
  cache_key = f'user-{self.id}_instance-{instance.id}_role'
244
254
  role = cache.get(cache_key)
245
255
  if role is None:
@@ -250,7 +260,8 @@ class User(AbstractBaseUser, SimoAdminMixin):
250
260
  ).first()
251
261
  if role:
252
262
  cache.set(cache_key, role, 20)
253
- return role
263
+ self._instance_roles[instance.id] = role
264
+ return self._instance_roles[instance.id]
254
265
 
255
266
  @property
256
267
  def role_id(self):
@@ -285,12 +296,20 @@ class User(AbstractBaseUser, SimoAdminMixin):
285
296
  'role': role
286
297
  }
287
298
  )
299
+ self._role_id = None
300
+ try:
301
+ cache.delete(f'user-{self.id}_instance-{instance.id}-role-id')
302
+ except:
303
+ pass
288
304
 
289
305
  @property
290
306
  def instances(self):
291
307
  from simo.core.models import Instance
292
308
  if not self.is_active:
293
309
  return Instance.objects.none()
310
+ if self._instances != None:
311
+ return self._instances
312
+
294
313
  cache_key = f'user-{self.id}_instances'
295
314
  instances = cache.get(cache_key)
296
315
  if instances is None:
@@ -303,7 +322,8 @@ class User(AbstractBaseUser, SimoAdminMixin):
303
322
  )
304
323
  ], is_active=True)
305
324
  cache.set(cache_key, instances, 10)
306
- return instances
325
+ self._instances = instances
326
+ return self._instances
307
327
 
308
328
  @property
309
329
  def component_permissions(self):
@@ -314,6 +334,8 @@ class User(AbstractBaseUser, SimoAdminMixin):
314
334
 
315
335
  @property
316
336
  def is_active(self):
337
+ if self._is_active != None:
338
+ return self._is_active
317
339
  cache_key = f'user-{self.id}_is_active'
318
340
  cached_value = cache.get(cache_key)
319
341
  if cached_value is None:
@@ -339,7 +361,9 @@ class User(AbstractBaseUser, SimoAdminMixin):
339
361
  self.instance_roles.filter(is_active=True).count()
340
362
  )
341
363
  cache.set(cache_key, cached_value, 20)
342
- return cached_value
364
+
365
+ self._is_active = cached_value
366
+ return self._is_active
343
367
 
344
368
 
345
369
  @is_active.setter
@@ -359,6 +383,8 @@ class User(AbstractBaseUser, SimoAdminMixin):
359
383
 
360
384
  rebuild_authorized_keys()
361
385
 
386
+ self._is_active = None
387
+
362
388
 
363
389
  @property
364
390
  def is_superuser(self):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: simo
3
- Version: 2.5.40
3
+ Version: 2.5.42
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
@@ -33,7 +33,7 @@ simo/backups/migrations/__pycache__/0004_alter_backup_options_alter_backuplog_op
33
33
  simo/backups/migrations/__pycache__/__init__.cpython-38.pyc,sha256=Lz1fs6V05h2AoxTOLNye0do9bEMnyuaXB_hHOjG5-HU,172
34
34
  simo/core/__init__.py,sha256=_s2TjJfQImsMrTIxqLAx9AZie1Ojmm6sCHASdl3WLGU,50
35
35
  simo/core/admin.py,sha256=T-NjMq1OxtCt-LjWL-YuuGtAi4JcvtjWhcGrLb8G5D4,18434
36
- simo/core/api.py,sha256=7qaj5RtA5LFbe5NR-oPXO2pLQ_ohyGx-_-rf5j83vSw,28479
36
+ simo/core/api.py,sha256=HWyzmFGbULndSaW3sdrQa_kw2zfjd7yzfYxErAij7cY,29070
37
37
  simo/core/api_auth.py,sha256=vCxvczA8aWNcW0VyKs5WlC_ytlqeGP_H_hkKUNVkCwM,1247
38
38
  simo/core/api_meta.py,sha256=EaiY-dCADP__9MvLpoHvhjytFT92IrxPZDv95xgqasU,4955
39
39
  simo/core/app_widgets.py,sha256=VxZzapuc-a29wBH7JzpvNF2SK1ECrgNUySId5ke1ffc,2509
@@ -53,9 +53,9 @@ simo/core/loggers.py,sha256=EBdq23gTQScVfQVH-xeP90-wII2DQFDjoROAW6ggUP4,1645
53
53
  simo/core/managers.py,sha256=n-b3I4uXzfHKTeB1VMjSaMsDUxp8FegFJwnbV1IsWQ4,3019
54
54
  simo/core/middleware.py,sha256=eUFf6iP-Snx_0TE3MoXsSwqrd5IjlukqZk2GQGStRCo,3385
55
55
  simo/core/models.py,sha256=QNVTnWeHAW6LVrs3eaR7WNMhwjICuQpXZ8vyH_2qCBo,22834
56
- simo/core/permissions.py,sha256=V47wzxjbEFfp5JUCD7_T4BQfDOFgcNqJcqmiItkEQsU,3008
56
+ simo/core/permissions.py,sha256=2YNRot2qoHjHKWPGOpO4PBseecctPbTlUQpepnFkCRs,3027
57
57
  simo/core/routing.py,sha256=X1_IHxyA-_Q7hw1udDoviVP4_FSBDl8GYETTC2zWTbY,499
58
- simo/core/serializers.py,sha256=Krnkov_Mmu9VMakoJbEB3HZDjMbd20pAdVUNFlNBoPg,21826
58
+ simo/core/serializers.py,sha256=NgqZhBjY0or6dfN-N_EaaygR_EUoUBryuK1DbD4R3Fg,21810
59
59
  simo/core/signal_receivers.py,sha256=2WfF3FI5ZmJM1S-oT_1w3TdnBUX6fjbI4Rpg-DKsuYA,8696
60
60
  simo/core/socket_consumers.py,sha256=trRZvBGTJ7xIbfdmVvn7zoiWp_qssSkMZykDrI5YQyE,9783
61
61
  simo/core/storage.py,sha256=_5igjaoWZAiExGWFEJMElxUw55DzJG1jqFty33xe8BE,342
@@ -66,7 +66,7 @@ simo/core/views.py,sha256=yx9I0byeVUa-LAOnklpWIYwpNNOf5m9fyjKBvj4YCh4,2475
66
66
  simo/core/widgets.py,sha256=J9e06C6I22F6xKic3VMgG7WeX07glAcl-4bF2Mg180A,2827
67
67
  simo/core/__pycache__/__init__.cpython-38.pyc,sha256=ZJFM_XN0RmJMULQulgA_wFiOnEtsMoedcOWnXjH-Y8o,208
68
68
  simo/core/__pycache__/admin.cpython-38.pyc,sha256=mXU-u9Bl5RCa0K4Y9wJAkYns33XCByME3sb_FsMC2DI,14090
69
- simo/core/__pycache__/api.cpython-38.pyc,sha256=2OVjqi3noPxW9y8yEPKcKfS4Jpxk437neASU2Ws8-dE,21836
69
+ simo/core/__pycache__/api.cpython-38.pyc,sha256=0kALbhViLcVqxcxzc_mmT9TzZcONc0ZNzZCWscbxjWE,22459
70
70
  simo/core/__pycache__/api_auth.cpython-38.pyc,sha256=mi3mu5qEKio_PvfQEvr3Q6AhdPLAHxzxAxrMbAz_pKU,1712
71
71
  simo/core/__pycache__/api_meta.cpython-38.pyc,sha256=VYx5ZeDyNBI4B_CBEIhV5B3GnLsMOx9s3rNZTSMODco,3703
72
72
  simo/core/__pycache__/app_widgets.cpython-38.pyc,sha256=oN657XMMZ6GYN9nblv7fX3kdnTEzSP9XV6PXM6Z0wl4,4358
@@ -86,13 +86,13 @@ simo/core/__pycache__/loggers.cpython-38.pyc,sha256=Z-cdQnC6XlIonPV4Sl4E52tP4NME
86
86
  simo/core/__pycache__/managers.cpython-38.pyc,sha256=6RTIxyjOgpQGtAqcUyE2vFPS09w1V5Wmd_vOV7rHRRI,3370
87
87
  simo/core/__pycache__/middleware.cpython-38.pyc,sha256=g3d4L2PwxFyRKIPMP9Hkdjk1PL9NarQd4hSHS55I8n8,2649
88
88
  simo/core/__pycache__/models.cpython-38.pyc,sha256=jLZXRDsR1_GJBvuDZ32KpuFz3B5qRoQmgpY6kLxxFI0,18554
89
- simo/core/__pycache__/permissions.cpython-38.pyc,sha256=cPBxpwmxEWbcwznNobVlBffo2DttT6YJ6ZFMOkEHpfs,2982
89
+ simo/core/__pycache__/permissions.cpython-38.pyc,sha256=UdtxCTXPEbe99vgZOfRz9wfKSYvUn9hSRbpIV9CJSyI,2988
90
90
  simo/core/__pycache__/routing.cpython-38.pyc,sha256=3T3FPJ8Cn99xZCGvMyg2xjl7al-Shm9CelbSpkJtNP8,599
91
- simo/core/__pycache__/serializers.cpython-38.pyc,sha256=sZx1gCy-0bdi0tZ5NatK7yGUcpI-d-T7o_mvhUpuOHM,19424
91
+ simo/core/__pycache__/serializers.cpython-38.pyc,sha256=QvlVp7m13i3Vn1FOmjV-y_IpLfmcSIy74jhb66ecJbo,19403
92
92
  simo/core/__pycache__/signal_receivers.cpython-38.pyc,sha256=Od1ejof2nHAFzTAG5vGGVjMA8WUJNVJ9o_KWGqRSR34,6669
93
93
  simo/core/__pycache__/socket_consumers.cpython-38.pyc,sha256=KqbO1cOewodVPcy0-htVefyUjCuELKV0o7fOfYqfgPc,8490
94
94
  simo/core/__pycache__/storage.cpython-38.pyc,sha256=9R1Xu0FJDflfRXUPsqEgt0SpwiP7FGk7HaR8s8XRyI8,721
95
- simo/core/__pycache__/tasks.cpython-38.pyc,sha256=A2qCdGJtaM1vpiM7LmzAMsuPkt5EG4vLzW2s0-WnklE,11220
95
+ simo/core/__pycache__/tasks.cpython-38.pyc,sha256=GFIC8IsQ_bK4rgNRl_oGbMCQHhbcgGS7dx-rjPcLy08,11220
96
96
  simo/core/__pycache__/todos.cpython-38.pyc,sha256=lOqGZ58siHM3isoJV4r7sg8igrfE9fFd-jSfeBa0AQI,253
97
97
  simo/core/__pycache__/views.cpython-38.pyc,sha256=IRjbX3MwKkAb10sMIJ3esKZH8W-tHwnuzm-mLIT_NWc,2584
98
98
  simo/core/__pycache__/widgets.cpython-38.pyc,sha256=sR0ZeHCHrhnNDBJuRrxp3zUsfBp0xrtF0xrK2TkQv1o,3520
@@ -153,7 +153,7 @@ simo/core/management/_hub_template/hub/celeryc.py,sha256=3ksDXftIZKJ4Cq9WNKJERdZ
153
153
  simo/core/management/_hub_template/hub/manage.py,sha256=PNNlw3EVeIJDgkG0l-klqoxsKWfTYWG9jzRG0upmAaI,620
154
154
  simo/core/management/_hub_template/hub/nginx.conf,sha256=40hvXL42MeiqqkLURNcDQsRudv1dNFLJnvb2-Y3RCkk,2394
155
155
  simo/core/management/_hub_template/hub/settings.py,sha256=4QhvhbtLRxHvAntwqG_qeAAtpDUqKvN4jzw9u3vqff8,361
156
- simo/core/management/_hub_template/hub/supervisor.conf,sha256=sfeitUI6V4MgPDtfj-6AEQSTS_VNvLUdwNYNdSI1zRI,2377
156
+ simo/core/management/_hub_template/hub/supervisor.conf,sha256=-xFtt7cVyY-SCZ_6Z4yI1-1LNtd0f0EAM9zB5VbY9i8,2362
157
157
  simo/core/management/_hub_template/hub/urls.py,sha256=Ydm-1BkYAzWeEF-MKSDIFf-7aE4qNLPm48-SA51XgJQ,25
158
158
  simo/core/management/_hub_template/hub/wsgi.py,sha256=Lo-huLHnMDTxSmMBOodVFMWBls9poddrV2KRzXU0xGo,280
159
159
  simo/core/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -10227,8 +10227,8 @@ simo/fleet/api.py,sha256=bK-j762v-KsPIvdh2SQCVC3TZ0D_RZgAaIyOfyNifeU,3108
10227
10227
  simo/fleet/auto_urls.py,sha256=UX66eR2ykMqFgfIllW-RTdjup5-FieCWl_BVm3CcXKg,702
10228
10228
  simo/fleet/base_types.py,sha256=wL9RVkHr0gA7HI1wZq0pruGEIgvQqpfnCL4cC3ywsvw,102
10229
10229
  simo/fleet/ble.py,sha256=eHA_9ABjbmH1vUVCv9hiPXQL2GZZSEVwfO0xyI1S0nI,1081
10230
- simo/fleet/controllers.py,sha256=fjri1GtCnflkkDpNqhTwy6i9CK6RDEB0Q_BtADzcG8E,29156
10231
- simo/fleet/forms.py,sha256=Z2iTfV_3Ui2QS2mQX7vPtLqiHvHprwkVSeyJ45c0aeU,64047
10230
+ simo/fleet/controllers.py,sha256=w2Zf-hFXyaaekoHbar7pIfE0iGN0S__79rANFm23hW8,29157
10231
+ simo/fleet/forms.py,sha256=S0OsfQ6VOm6lox2gSTKjRrIH0mlPcsAUhaplm6Wsiuw,64772
10232
10232
  simo/fleet/gateways.py,sha256=lKEJW0MgaOEiNnijH50DNSVChvaUT3TA3UurcI57P8k,5677
10233
10233
  simo/fleet/managers.py,sha256=ZNeHFSkF5kzsl9E1DCBevOW6kXJlD6kw0LU4B-JMOG8,828
10234
10234
  simo/fleet/models.py,sha256=zPplx_v64nfKBmb-nCb74aCVtEeY3m3SjEy-VhbnydU,17511
@@ -10244,8 +10244,8 @@ simo/fleet/__pycache__/api.cpython-38.pyc,sha256=Ntc1sYKZMygW2eNTxVUgoeCF3StoUZc
10244
10244
  simo/fleet/__pycache__/auto_urls.cpython-38.pyc,sha256=Tc6a6BCXHjijP8U2jE2ghlJwnSNrGm59-hW5t-80wF0,689
10245
10245
  simo/fleet/__pycache__/base_types.cpython-38.pyc,sha256=deyPwjpT6xZiFxBGFnj5b7R-lbdOTh2krgpJhrcGVhc,274
10246
10246
  simo/fleet/__pycache__/ble.cpython-38.pyc,sha256=Nrof9w7cm4OlpFWHeVnmvvanh2_oF9oQ3TknJiV93-0,1267
10247
- simo/fleet/__pycache__/controllers.cpython-38.pyc,sha256=jtFHr_uyjCCeuidL-o-hGaf21u0fnxK_O6hTRdY6lpc,24906
10248
- simo/fleet/__pycache__/forms.cpython-38.pyc,sha256=asyDqRo5kRIaFhWqDRXqBPxKGl43O6O00dXOemUN6p0,43057
10247
+ simo/fleet/__pycache__/controllers.cpython-38.pyc,sha256=jpXIYOPPQyumLwiO-PbgB8L5WYKkvd5T5GlAFnBKHp8,24906
10248
+ simo/fleet/__pycache__/forms.cpython-38.pyc,sha256=yyRnT_AU1kCLo3ISyuXonzP8hoiu8SAvRxhu9WQPIoc,43550
10249
10249
  simo/fleet/__pycache__/gateways.cpython-38.pyc,sha256=0RKVn0ndreVKhsrukqeLPSdMnRrsQ_W7yeVeBkRLfIk,5058
10250
10250
  simo/fleet/__pycache__/managers.cpython-38.pyc,sha256=Vmm23zoQnS3-uS5_WJt2n3wtjhLiEhLWaYxXJCU6Gts,1339
10251
10251
  simo/fleet/__pycache__/models.cpython-38.pyc,sha256=WUahZgETWlem5rVXlJ_vINFRM7OZWp5xpWXGMoeBXsM,14131
@@ -10346,8 +10346,8 @@ simo/fleet/templates/fleet/controllers_info/ENS160AirQualitySensor.md,sha256=3LS
10346
10346
  simo/generic/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10347
10347
  simo/generic/app_widgets.py,sha256=TPRLj4hri2hBuY6mrdwBiv-01z2hDxZmsup-GDD9LrM,953
10348
10348
  simo/generic/base_types.py,sha256=u3SlfpNYaCwkVBwomWgso4ODzL71ay9MhiAW-bxgnDU,341
10349
- simo/generic/controllers.py,sha256=QwmVXC3phIDYXAP98MtKbzHuamWPc1kVg9FQfjkP22g,50566
10350
- simo/generic/forms.py,sha256=tCbIZtbruBHjZRzulGXJQOjmxaGJ2uoKqjT1scScdDQ,29004
10349
+ simo/generic/controllers.py,sha256=VZLa9PcGugE3E_WG6TV1zsn3h3Cj0LHBcTahm4KI6uY,50702
10350
+ simo/generic/forms.py,sha256=AzJ26n01gucuuBbh9RE4jZSG1wxKAWdui7dndXfxSKc,29229
10351
10351
  simo/generic/gateways.py,sha256=0gDjWSCAt6MABR6k8xV7N1zXutDLCileGtAm6pSl4Q4,16165
10352
10352
  simo/generic/models.py,sha256=Adq7ipWK-renxJlNW-SZnAq2oGEOwKx8EdUWaKnfcVQ,7597
10353
10353
  simo/generic/routing.py,sha256=elQVZmgnPiieEuti4sJ7zITk1hlRxpgbotcutJJgC60,228
@@ -10355,22 +10355,24 @@ simo/generic/socket_consumers.py,sha256=K2OjphIhKJH48BvfFfoCOyCQZ1NmXb_phs6y1IP-
10355
10355
  simo/generic/__pycache__/__init__.cpython-38.pyc,sha256=mLu54WS9KIl-pHwVCBKpsDFIlOqml--JsOVzAUHg6cU,161
10356
10356
  simo/generic/__pycache__/app_widgets.cpython-38.pyc,sha256=YZ5db6-FPynBi6ooPW5crK9lZ6ymRh2DlGN6FwxfX4M,1820
10357
10357
  simo/generic/__pycache__/base_types.cpython-38.pyc,sha256=aV5NdIuvXR-ItKpI__MwcyPZHD6Z882TFdgYkPCkr1I,493
10358
- simo/generic/__pycache__/controllers.cpython-38.pyc,sha256=f0nzxm3U7pgmn7TbUOM3KfIWk50II2XeGbkCyFDix_w,33658
10359
- simo/generic/__pycache__/forms.cpython-38.pyc,sha256=Dd-hgbRNdIFzUDjSZ4yQp7wwR-ILBYQFI8MCob_ZYwQ,21232
10358
+ simo/generic/__pycache__/controllers.cpython-38.pyc,sha256=QjpmK-tGfbHHkK7grgDDRHqKoNNIUsU2cDnv0QOYFdA,33736
10359
+ simo/generic/__pycache__/forms.cpython-38.pyc,sha256=smUAAhnyhuadkSC-VOYaZ96F9op_QGOTGHuuw25IXMI,21543
10360
10360
  simo/generic/__pycache__/gateways.cpython-38.pyc,sha256=uubZtp-UgEmKU4vG9ophnCZFEDqmKkeY1yyWUXzdmdY,12015
10361
10361
  simo/generic/__pycache__/models.cpython-38.pyc,sha256=MZpum7syAFxuulf47K7gtUlJJ7xRD-IBUBAwUM1ZRnw,5825
10362
10362
  simo/generic/__pycache__/routing.cpython-38.pyc,sha256=xtxTUTBTdivzFyA5Wh7k-hUj1WDO_FiRq6HYXdbr9Ks,382
10363
10363
  simo/generic/__pycache__/socket_consumers.cpython-38.pyc,sha256=qJO5kvQLWhsQDOr1AtAtsAybuRWioxSkQei3Pc7rdP0,1737
10364
10364
  simo/generic/migrations/0001_initial.py,sha256=7FpPcfpRU5ya0b8s2KbxR5a3npf92YruvZltUybjzys,676
10365
+ simo/generic/migrations/0002_auto_20241126_0726.py,sha256=SX38JwP732QooOm5HM1Xo7Th_Mv_6YZloT3eozULOhs,922
10365
10366
  simo/generic/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10366
10367
  simo/generic/migrations/__pycache__/0001_initial.cpython-38.pyc,sha256=xy_fN8vnebC_X8hArqHAOLYHLm3puD_stzJ-LvUOhgk,1009
10368
+ simo/generic/migrations/__pycache__/0002_auto_20241126_0726.cpython-38.pyc,sha256=vfPHUDBG6UlfdqsIi35ZcdQ0NfsSeawvTNzZ7YTKQd0,1130
10367
10369
  simo/generic/migrations/__pycache__/__init__.cpython-38.pyc,sha256=nJV0NkIT8MuONj1hUX-V6aCU2lX3BXHyPjisapnBsPA,172
10368
10370
  simo/generic/scripting/__init__.py,sha256=aZZvNBae7unnux_zGHCIWCV2z47hVJc-DIL72Hqfkeo,600
10369
10371
  simo/generic/scripting/example.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10370
10372
  simo/generic/scripting/helpers.py,sha256=iP-fxxB8HsFQy3k2CjFubu86aMqvWgmh-p24DiyOrek,4330
10371
10373
  simo/generic/scripting/serializers.py,sha256=PjyFrjdPK1mBsgbNhyqMi9SWzcymqTa742ipy0LhAN4,1996
10372
10374
  simo/generic/scripting/__pycache__/__init__.cpython-38.pyc,sha256=eHncoNpv5dy35IO1_htWd8CK0sHFBnU_WJ0hl5TKOHQ,794
10373
- simo/generic/scripting/__pycache__/helpers.cpython-38.pyc,sha256=34sa3L2cK1Aw636PCaoCYIWUBIE1h6XmbgDIto9cLeo,2757
10375
+ simo/generic/scripting/__pycache__/helpers.cpython-38.pyc,sha256=Rr43Tp0Y4feRu7Ho_Pz1D0rhbS0Rb0JlXxmxj7O5x-c,3634
10374
10376
  simo/generic/scripting/__pycache__/serializers.cpython-38.pyc,sha256=JD9KCNO27H18mkFaeSMdybTMdTvodqcZSLNbC3pheHU,3412
10375
10377
  simo/generic/static/weather_icons/01d@2x.png,sha256=TZfWi6Rfddb2P-oldWWcjUiuCHiU9Yrc5hyrQAhF26I,948
10376
10378
  simo/generic/static/weather_icons/01n@2x.png,sha256=e9RleTa0T7To9Wi2wJ-9waeTbfHOsUB_xGwkx-89eEg,945
@@ -10429,13 +10431,13 @@ simo/multimedia/migrations/__pycache__/0004_auto_20231023_1055.cpython-38.pyc,sh
10429
10431
  simo/multimedia/migrations/__pycache__/__init__.cpython-38.pyc,sha256=mCgSiQBphL85imdWyTi9-4zBDYF6HfXbhB0ycSPVVuA,175
10430
10432
  simo/notifications/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10431
10433
  simo/notifications/admin.py,sha256=WQbN_bd2KRxVjbOajeworNrV9QlDNSadQT58La0Nn2M,1174
10432
- simo/notifications/api.py,sha256=GXQpq68ULBaJpU8w3SJKaCKuxYGWYehKnGeocGB1RVc,1783
10434
+ simo/notifications/api.py,sha256=qzM-P3kq_YQ8_wHS8EMIutRbTvtqOXczQbJFb7EgQ08,1773
10433
10435
  simo/notifications/models.py,sha256=QGDLGAi5gk8OTcvd7ho5WNdctDymWGmGF1ZqN4-G_ZA,2443
10434
10436
  simo/notifications/serializers.py,sha256=altDEAPWwOhxRcEzE9-34jL8EFpyf3vPoEdAPoVLfGc,523
10435
10437
  simo/notifications/utils.py,sha256=uBl-Y7WGu00iaGM5rrdogcq0OMRVtyVfJF39-mdB3_k,1853
10436
10438
  simo/notifications/__pycache__/__init__.cpython-38.pyc,sha256=YvucUfu98XFvEEg1LYFMlOZJpo_jSGxTVrM-ylAFLOg,167
10437
10439
  simo/notifications/__pycache__/admin.cpython-38.pyc,sha256=MScNrtVM1wavefsPfxy0A7LVyXKcbvEkLH9GJkgNOl8,1945
10438
- simo/notifications/__pycache__/api.cpython-38.pyc,sha256=ys6E4AFghX6bq-rQ0gtA9s0Y2Hh-ypsWH8-Yz4edMrc,2073
10440
+ simo/notifications/__pycache__/api.cpython-38.pyc,sha256=8dPiIMaFQlBFP01VwXbQ98udaZaqwibZJtz_Y2cXa8U,2073
10439
10441
  simo/notifications/__pycache__/models.cpython-38.pyc,sha256=PoqLuOnlaAWQQ-20AtqhvAlLSkakPmdn7J7wGvHNW3g,2449
10440
10442
  simo/notifications/__pycache__/serializers.cpython-38.pyc,sha256=7-eRGKYuQ4g1SpKOMpz17SIiu1HmaMoYv-cJbaO9QGA,1028
10441
10443
  simo/notifications/__pycache__/utils.cpython-38.pyc,sha256=6Tq7VkW-pZLzWzcxPtBU9MDFZLO7iLY8-ygyefoJ5OQ,1529
@@ -10456,7 +10458,7 @@ simo/users/auto_urls.py,sha256=lcJvteBsbHQMJieZpDz-63tDYejLApqsW3CUnDakd7k,272
10456
10458
  simo/users/dynamic_settings.py,sha256=sEIsi4yJw3kH46Jq_aOkSuK7QTfQACGUE-lkyBogCaM,570
10457
10459
  simo/users/managers.py,sha256=OHgEP85MBtdkdYxdstBd8RavTBT8F_2WyDxUJ9aCqqM,246
10458
10460
  simo/users/middleware.py,sha256=GMCrnWSc_2qCleyQIkfQGdL-pU-UTEcSg1wPvIKZ9uk,1210
10459
- simo/users/models.py,sha256=2xQetyY9Mw6b7CidOC1IUd_Hc_ZgUGvqqgTpDxffnNI,19144
10461
+ simo/users/models.py,sha256=2c9unF8xd9kjwzwQEUEsxw79NrO1_YSJZl6h4wpNsI8,19915
10460
10462
  simo/users/permissions.py,sha256=IwtYS8yQdupWbYKR9VimSRDV3qCJ2jXP57Lyjpb2EQM,242
10461
10463
  simo/users/serializers.py,sha256=zzw1KONTnaTNBaU0r4rNVxJ827KzD6Z5LuQt27ZsQ98,2516
10462
10464
  simo/users/sso_urls.py,sha256=gQOaPvGMYFD0NCVSwyoWO-mTEHe5j9sbzV_RK7kdvp0,251
@@ -10473,7 +10475,7 @@ simo/users/__pycache__/auto_urls.cpython-38.pyc,sha256=K-3sz2h-cEitoflSmZk1t0eUg
10473
10475
  simo/users/__pycache__/dynamic_settings.cpython-38.pyc,sha256=6F8JBjZkHykySnmZjNEzjS0ijbmPdcp9yUAZ5kqq_Fo,864
10474
10476
  simo/users/__pycache__/managers.cpython-38.pyc,sha256=O0Y8ABp42RAosrbODmYsPMaj9AyOPyJ-aqzuO0Qpi2s,679
10475
10477
  simo/users/__pycache__/middleware.cpython-38.pyc,sha256=Tj4nVEAvxEW3xA63fBRiJWRJpz_M848ZOqbHioc_IPE,1149
10476
- simo/users/__pycache__/models.cpython-38.pyc,sha256=NlcBNkYjDv_1BE60uA8iHrQEl1xBvXZFnU-MZisl0qw,17344
10478
+ simo/users/__pycache__/models.cpython-38.pyc,sha256=UH-lceJI5fYxYn85dIUJ8HYESwb9irIPkvu_d-vGSkA,17777
10477
10479
  simo/users/__pycache__/permissions.cpython-38.pyc,sha256=ez5NxoL_JUeeH6GsKhvFreuA3FCBgGf9floSypdXUtM,633
10478
10480
  simo/users/__pycache__/serializers.cpython-38.pyc,sha256=Dy8RAcwNkNSXoJHvLp8fozURyHCtucqpSPyqZtbnMZc,3732
10479
10481
  simo/users/__pycache__/sso_urls.cpython-38.pyc,sha256=uAwDozpOmrhUald-8tOHANILXkH7-TI8fNYXOtPkSY8,402
@@ -10579,9 +10581,9 @@ simo/users/templates/invitations/expired_msg.html,sha256=47DEQpj8HBSa-_TImW-5JCe
10579
10581
  simo/users/templates/invitations/expired_suggestion.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10580
10582
  simo/users/templates/invitations/taken_msg.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10581
10583
  simo/users/templates/invitations/taken_suggestion.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10582
- simo-2.5.40.dist-info/LICENSE.md,sha256=M7wm1EmMGDtwPRdg7kW4d00h1uAXjKOT3HFScYQMeiE,34916
10583
- simo-2.5.40.dist-info/METADATA,sha256=PEe6jttMZzUykEGtSSMfd-mmaTTeG-DXGylAHEkaEzI,1953
10584
- simo-2.5.40.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
10585
- simo-2.5.40.dist-info/entry_points.txt,sha256=S9PwnUYmTSW7681GKDCxUbL0leRJIaRk6fDQIKgbZBA,135
10586
- simo-2.5.40.dist-info/top_level.txt,sha256=GmS1hrAbpVqn9OWZh6UX82eIOdRLgYA82RG9fe8v4Rs,5
10587
- simo-2.5.40.dist-info/RECORD,,
10584
+ simo-2.5.42.dist-info/LICENSE.md,sha256=M7wm1EmMGDtwPRdg7kW4d00h1uAXjKOT3HFScYQMeiE,34916
10585
+ simo-2.5.42.dist-info/METADATA,sha256=m8N7A6bxlyLXR7LdqGWZJjNZ7Oo8vS8jq-yCKPLHFEQ,1953
10586
+ simo-2.5.42.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
10587
+ simo-2.5.42.dist-info/entry_points.txt,sha256=S9PwnUYmTSW7681GKDCxUbL0leRJIaRk6fDQIKgbZBA,135
10588
+ simo-2.5.42.dist-info/top_level.txt,sha256=GmS1hrAbpVqn9OWZh6UX82eIOdRLgYA82RG9fe8v4Rs,5
10589
+ simo-2.5.42.dist-info/RECORD,,
File without changes