simo 2.7.15__py3-none-any.whl → 2.7.16__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 (30) hide show
  1. simo/__pycache__/urls.cpython-312.pyc +0 -0
  2. simo/automation/__pycache__/gateways.cpython-312.pyc +0 -0
  3. simo/automation/gateways.py +5 -2
  4. simo/core/migrations/0049_alter_gateway_type.py +18 -0
  5. simo/core/migrations/__pycache__/0049_alter_gateway_type.cpython-312.pyc +0 -0
  6. simo/generic/__pycache__/controllers.cpython-312.pyc +0 -0
  7. simo/generic/__pycache__/forms.cpython-312.pyc +0 -0
  8. simo/generic/__pycache__/gateways.cpython-312.pyc +0 -0
  9. simo/generic/controllers.py +10 -0
  10. simo/generic/forms.py +13 -10
  11. simo/generic/gateways.py +43 -43
  12. simo/multimedia/__pycache__/admin.cpython-312.pyc +0 -0
  13. simo/multimedia/__pycache__/auto_urls.cpython-312.pyc +0 -0
  14. simo/multimedia/__pycache__/controllers.cpython-312.pyc +0 -0
  15. simo/multimedia/__pycache__/models.cpython-312.pyc +0 -0
  16. simo/multimedia/__pycache__/views.cpython-312.pyc +0 -0
  17. simo/multimedia/admin.py +7 -7
  18. simo/multimedia/auto_urls.py +5 -2
  19. simo/multimedia/controllers.py +16 -12
  20. simo/multimedia/migrations/0006_remove_sound_length_sound_duration.py +22 -0
  21. simo/multimedia/migrations/__pycache__/0006_remove_sound_length_sound_duration.cpython-312.pyc +0 -0
  22. simo/multimedia/models.py +8 -4
  23. simo/multimedia/views.py +63 -1
  24. simo/urls.py +6 -2
  25. {simo-2.7.15.dist-info → simo-2.7.16.dist-info}/METADATA +1 -1
  26. {simo-2.7.15.dist-info → simo-2.7.16.dist-info}/RECORD +30 -26
  27. {simo-2.7.15.dist-info → simo-2.7.16.dist-info}/LICENSE.md +0 -0
  28. {simo-2.7.15.dist-info → simo-2.7.16.dist-info}/WHEEL +0 -0
  29. {simo-2.7.15.dist-info → simo-2.7.16.dist-info}/entry_points.txt +0 -0
  30. {simo-2.7.15.dist-info → simo-2.7.16.dist-info}/top_level.txt +0 -0
Binary file
@@ -240,14 +240,16 @@ class AutomationsGatewayHandler(GatesHandler, BaseObjectCommandsGatewayHandler):
240
240
 
241
241
  comp = Component.objects.filter(id=id).first()
242
242
  if comp and comp.value == 'finished':
243
+ if process.is_alive():
244
+ process.kill()
243
245
  self.running_scripts.pop(id)
244
246
  continue
245
247
 
246
248
  if process.is_alive():
247
249
  if not comp and id not in self.terminating_scripts:
248
250
  # script is deleted and was not properly called to stop
249
- self.running_scripts.pop(id)
250
251
  process.kill()
252
+ self.running_scripts.pop(id)
251
253
  continue
252
254
  else:
253
255
  self.last_death = time.time()
@@ -261,7 +263,6 @@ class AutomationsGatewayHandler(GatesHandler, BaseObjectCommandsGatewayHandler):
261
263
  comp.value = 'error'
262
264
  comp.save()
263
265
 
264
-
265
266
  if self.last_death and time.time() - self.last_death < 5:
266
267
  # give 10s air before we wake these dead scripts up!
267
268
  return
@@ -349,6 +350,7 @@ class AutomationsGatewayHandler(GatesHandler, BaseObjectCommandsGatewayHandler):
349
350
  print("START SCRIPT %s" % str(component))
350
351
  if component.id in self.running_scripts:
351
352
  if component.value in ('finished', 'error', 'stopped'):
353
+ self.running_scripts[component.id].kill()
352
354
  self.running_scripts.pop(component.id)
353
355
  elif component.id not in self.terminating_scripts \
354
356
  and self.running_scripts[component.id].is_alive():
@@ -356,6 +358,7 @@ class AutomationsGatewayHandler(GatesHandler, BaseObjectCommandsGatewayHandler):
356
358
  component.value = 'running'
357
359
  component.save()
358
360
  return
361
+ self.running_scripts[component.id].kill()
359
362
 
360
363
  self.running_scripts[component.id] = ScriptRunHandler(
361
364
  component.id, multiprocessing.Event(),
@@ -0,0 +1,18 @@
1
+ # Generated by Django 4.2.10 on 2024-12-22 07:42
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ('core', '0048_publicfile_privatefile'),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AlterField(
14
+ model_name='gateway',
15
+ name='type',
16
+ field=models.CharField(choices=[('simo.automation.gateways.AutomationsGatewayHandler', 'Automation'), ('simo_heos.gateways.HEOSGatewayHandler', 'DENON HEOS'), ('simo.generic.gateways.DummyGatewayHandler', 'Dummy'), ('simo.generic.gateways.GenericGatewayHandler', 'Generic'), ('simo_komfovent.gateways.KomfoventGatewayHandler', 'Komfovent'), ('simo.fleet.gateways.FleetGatewayHandler', 'SIMO.io Fleet')], db_index=True, max_length=200, unique=True),
17
+ ),
18
+ ]
@@ -1059,6 +1059,16 @@ class AudioAlert(Switch):
1059
1059
  name = _("Audio Alert")
1060
1060
  config_form = AudioAlertConfigForm
1061
1061
 
1062
+ def send(self, value):
1063
+ for player in Component.objects.filter(
1064
+ id__in=self.component.config['players']
1065
+ ):
1066
+ if value:
1067
+ player.play_alert(self.component.id)
1068
+ else:
1069
+ self.component.set(False)
1070
+ player.cancel_alert()
1071
+
1062
1072
 
1063
1073
  class StateSelect(ControllerBase):
1064
1074
  gateway_class = GenericGatewayHandler
simo/generic/forms.py CHANGED
@@ -7,7 +7,7 @@ from django.db.models import Q
7
7
  from django.utils.translation import gettext_lazy as _
8
8
  from django.core.files.uploadedfile import InMemoryUploadedFile
9
9
  from simo.core.forms import HiddenField, BaseComponentForm
10
- from simo.core.models import Icon, Component, PublicFile
10
+ from simo.core.models import Component
11
11
  from simo.core.controllers import (
12
12
  NumericSensor, MultiSensor, Switch, Dimmer
13
13
  )
@@ -22,6 +22,7 @@ from simo.core.form_fields import (
22
22
  )
23
23
  from simo.core.forms import DimmerConfigForm, SwitchForm
24
24
  from simo.core.form_fields import SoundField
25
+ from simo.multimedia.models import Sound
25
26
 
26
27
  ACTION_METHODS = (
27
28
  ('turn_on', "Turn ON"), ('turn_off', "Turn OFF"),
@@ -670,24 +671,26 @@ class AudioAlertConfigForm(BaseComponentForm):
670
671
 
671
672
  return self.cleaned_data['sound']
672
673
 
673
-
674
674
  def save(self, commit=True):
675
675
  obj = super().save(commit=commit)
676
676
  if type(self.cleaned_data['sound']) != InMemoryUploadedFile:
677
677
  return obj
678
678
 
679
- public_file = PublicFile(component=obj)
680
- public_file.file.save(
679
+ sound = Sound(
680
+ name=self.cleaned_data['sound'].name,
681
+ duration=self.cleaned_data['sound'].duration
682
+ )
683
+ sound.file.save(
681
684
  self.cleaned_data['sound'].name, self.cleaned_data['sound'],
682
685
  save=True
683
686
  )
684
- org = PublicFile.objects.filter(
685
- id=self.instance.config.get('public_file_id', 0)
686
- )
687
- if org:
688
- org.delete()
689
- self.instance.config['public_file_id'] = public_file.id
687
+ Sound.objects.filter(
688
+ id=self.instance.config.get('sound_id', 0)
689
+ ).delete()
690
+ self.instance.config['sound_id'] = sound.id
690
691
  self.instance.config['duration'] = self.cleaned_data['sound'].duration
692
+ self.instance.config['stream_url'] = sound.stream_url()
693
+ self.instance.config['file_url'] = sound.get_absolute_url()
691
694
  self.instance.config['sound'] = self.cleaned_data['sound'].name
692
695
  self.instance.save()
693
696
  return obj
simo/generic/gateways.py CHANGED
@@ -130,49 +130,49 @@ class GroupButtonsHandler:
130
130
  group.toggle()
131
131
 
132
132
 
133
- class AudioAlertsHandler:
134
-
135
- def control_audio_alert(self, component, val):
136
- if val:
137
- public_file = PublicFile.objects.filter(
138
- component=component
139
- ).first()
140
- if not public_file:
141
- return
142
- uri = f"http://{get_self_ip()}{public_file.get_absolute_url()}"
143
- loop = component.config.get('loop', False)
144
- for pl_id in component.config.get('players', []):
145
- player = Component.objects.filter(
146
- id=pl_id, base_type='audio-player'
147
- ).first()
148
- if not player:
149
- continue
150
- player.play_alert(
151
- uri,
152
- component.config.get('loop', False),
153
- component.config.get('volume', 50)
154
- )
155
- if not loop:
156
- def set_done(comp):
157
- comp.set(False)
158
- threading.Timer(
159
- component.config.get('duration', 1),
160
- set_done, args=[component]
161
- )
162
- component.set(True)
163
- else:
164
- for pl_id in component.config.get('players', []):
165
- player = Component.objects.filter(
166
- id=pl_id, base_type='audio-player'
167
- ).first()
168
- if not player:
169
- continue
170
- player.cancel_alert()
171
- component.set(False)
133
+ # class AudioAlertsHandler:
134
+ #
135
+ # def control_audio_alert(self, component, val):
136
+ # if val:
137
+ # public_file = PublicFile.objects.filter(
138
+ # component=component
139
+ # ).first()
140
+ # if not public_file:
141
+ # return
142
+ # uri = f"http://{get_self_ip()}{public_file.get_absolute_url()}"
143
+ # loop = component.config.get('loop', False)
144
+ # for pl_id in component.config.get('players', []):
145
+ # player = Component.objects.filter(
146
+ # id=pl_id, base_type='audio-player'
147
+ # ).first()
148
+ # if not player:
149
+ # continue
150
+ # player.play_alert(
151
+ # uri,
152
+ # component.config.get('loop', False),
153
+ # component.config.get('volume', 50)
154
+ # )
155
+ # if not loop:
156
+ # def set_done(comp):
157
+ # comp.set(False)
158
+ # threading.Timer(
159
+ # component.config.get('duration', 1),
160
+ # set_done, args=[component]
161
+ # )
162
+ # component.set(True)
163
+ # else:
164
+ # for pl_id in component.config.get('players', []):
165
+ # player = Component.objects.filter(
166
+ # id=pl_id, base_type='audio-player'
167
+ # ).first()
168
+ # if not player:
169
+ # continue
170
+ # player.cancel_alert()
171
+ # component.set(False)
172
172
 
173
173
 
174
174
  class GenericGatewayHandler(
175
- BaseObjectCommandsGatewayHandler, GroupButtonsHandler, AudioAlertsHandler
175
+ BaseObjectCommandsGatewayHandler, GroupButtonsHandler
176
176
  ):
177
177
  name = "Generic"
178
178
  config_form = BaseGatewayForm
@@ -268,7 +268,7 @@ class GenericGatewayHandler(
268
268
 
269
269
  def on_mqtt_message(self, client, userdata, msg):
270
270
  print("Mqtt message: ", msg.payload)
271
- from simo.generic.controllers import AlarmGroup, AudioAlert
271
+ from simo.generic.controllers import AlarmGroup#, #AudioAlert
272
272
 
273
273
  payload = json.loads(msg.payload)
274
274
  drop_current_instance()
@@ -278,8 +278,8 @@ class GenericGatewayHandler(
278
278
  try:
279
279
  if component.controller_uid == AlarmGroup.uid:
280
280
  self.control_alarm_group(component, payload.get('set_val'))
281
- elif component.controller_uid == AudioAlert.uid:
282
- self.control_audio_alert(component, payload.get('set_val'))
281
+ # elif component.controller_uid == AudioAlert.uid:
282
+ # self.control_audio_alert(component, payload.get('set_val'))
283
283
  else:
284
284
  component.controller.set(payload.get('set_val'))
285
285
  except Exception:
simo/multimedia/admin.py CHANGED
@@ -7,23 +7,23 @@ from .forms import SoundModelForm
7
7
 
8
8
  @admin.register(Sound)
9
9
  class SoundAdmin(admin.ModelAdmin):
10
- list_display = 'id', 'name', 'file', 'length_display', 'date_uploaded'
10
+ list_display = 'id', 'name', 'file', 'duration_display', 'date_uploaded'
11
11
  search_fields = 'name', 'file'
12
12
  list_display_links = 'id', 'name',
13
13
  form = SoundModelForm
14
- readonly_fields = 'length_display',
14
+ readonly_fields = 'duration_display',
15
15
 
16
- def length_display(self, obj=None):
17
- if obj and obj.length != None:
18
- return str(timedelta(seconds=obj.length))
16
+ def duration_display(self, obj=None):
17
+ if obj and obj.duration != None:
18
+ return str(timedelta(seconds=obj.duration))
19
19
 
20
- length_display.short_description = 'length'
20
+ duration_display.short_description = 'duration'
21
21
 
22
22
  def save_model(self, request, obj, form, change):
23
23
  super().save_model(request, obj, form, change)
24
24
  # need to keep it here as using admin interface skips post_save signals
25
25
  try:
26
- obj.length = int(
26
+ obj.duration = int(
27
27
  librosa.core.get_duration(
28
28
  sr=22050, filename=obj.file.path
29
29
  )
@@ -1,10 +1,13 @@
1
1
  from django.urls import path, re_path
2
- from .views import SoundAutocomplete
2
+ from .views import SoundAutocomplete, sound_stream
3
3
 
4
4
 
5
5
  urlpatterns = [
6
6
  path(
7
- 'autocomplete-sound',
7
+ 'autocomplete-sound/',
8
8
  SoundAutocomplete.as_view(), name='autocomplete-sound'
9
+ ),
10
+ path(
11
+ 'sound-<int:sound_id>-stream/', sound_stream, name='sound-stream'
9
12
  )
10
13
  ]
@@ -90,18 +90,22 @@ class BasePlayer(Switch):
90
90
  assert 0 <= volume <= 100
91
91
  self.send({"play_uri": uri, 'volume': volume})
92
92
 
93
- def play_alert(self, val, loop=False, volume=None):
94
- '''
95
- Plays alert and goes back to whatever was playing initially
96
- :param val: uri
97
- :param loop: Repeat infinitely
98
- :param volume: volume at which to play
99
- :return:
100
- '''
101
- assert type(val) == str
102
- if volume:
103
- assert 0 <= volume <= 100
104
- self.send({"alert": val, 'loop': loop, 'volume': volume})
93
+ # def play_alert(self, val, loop=False, volume=None):
94
+ # '''
95
+ # Plays alert and goes back to whatever was playing initially
96
+ # :param val: uri
97
+ # :param loop: Repeat infinitely
98
+ # :param volume: volume at which to play
99
+ # :return:
100
+ # '''
101
+ # assert type(val) == str
102
+ # if volume:
103
+ # assert 0 <= volume <= 100
104
+ # self.send({"alert": val, 'loop': loop, 'volume': volume})
105
+
106
+
107
+ def play_alert(self, id):
108
+ self.send({"alert": id})
105
109
 
106
110
  def cancel_alert(self):
107
111
  '''Cancel alert if it's currently playing'''
@@ -0,0 +1,22 @@
1
+ # Generated by Django 4.2.10 on 2024-12-22 07:42
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ('multimedia', '0005_remove_sound_slug_sound_date_uploaded'),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.RemoveField(
14
+ model_name='sound',
15
+ name='length',
16
+ ),
17
+ migrations.AddField(
18
+ model_name='sound',
19
+ name='duration',
20
+ field=models.PositiveIntegerField(default=0, editable=False, help_text='Sound duration in seconds'),
21
+ ),
22
+ ]
simo/multimedia/models.py CHANGED
@@ -1,4 +1,5 @@
1
1
  import os, librosa
2
+ from django.urls import reverse
2
3
  from django.db import models
3
4
  from django.db.models.signals import post_save, post_delete
4
5
  from django.dispatch import receiver
@@ -15,8 +16,8 @@ class Sound(models.Model):
15
16
  )
16
17
  )
17
18
  note = models.TextField(null=True, blank=True)
18
- length = models.PositiveIntegerField(
19
- editable=False, default=0, help_text='Sound length in seconds'
19
+ duration = models.PositiveIntegerField(
20
+ editable=False, default=0, help_text='Sound duration in seconds'
20
21
  )
21
22
  date_uploaded = models.DateTimeField(auto_now_add=True)
22
23
 
@@ -26,11 +27,14 @@ class Sound(models.Model):
26
27
  def get_absolute_url(self):
27
28
  return self.file.url
28
29
 
30
+ def stream_url(self):
31
+ return reverse('sound-stream', kwargs={'sound_id': self.id})
32
+
29
33
 
30
34
  @receiver(post_save, sender=Sound)
31
35
  def determine_duration(sender, instance, created, **kwargs):
32
- if not instance.length:
33
- instance.length = int(
36
+ if not instance.duration:
37
+ instance.duration = int(
34
38
  librosa.core.get_duration(
35
39
  sr=22050, filename=instance.file.path
36
40
  )
simo/multimedia/views.py CHANGED
@@ -1,4 +1,9 @@
1
+ import re, os
1
2
  from django.http import Http404
3
+ from django.conf import settings
4
+ from wsgiref.util import FileWrapper
5
+ from django.shortcuts import get_object_or_404
6
+ from django.http import StreamingHttpResponse
2
7
  from dal import autocomplete
3
8
  from simo.core.utils.helpers import search_queryset
4
9
  from .models import Sound
@@ -16,4 +21,61 @@ class SoundAutocomplete(autocomplete.Select2QuerySetView):
16
21
  qs = qs.filter(pk__in=self.request.GET['value'].split(','))
17
22
  elif self.q:
18
23
  qs = search_queryset(qs, self.q, ('name', 'slug'))
19
- return qs
24
+ return qs
25
+
26
+
27
+
28
+
29
+
30
+ def file_iterator(file_path, chunk_size=8192, offset=0, length=None):
31
+ with open(file_path, "rb") as f:
32
+ f.seek(offset, os.SEEK_SET)
33
+ remaining = length
34
+ while True:
35
+ bytes_length = (
36
+ chunk_size
37
+ if remaining is None
38
+ else min(remaining, chunk_size)
39
+ )
40
+ data = f.read(bytes_length)
41
+ if not data:
42
+ break
43
+ if remaining:
44
+ remaining -= len(data)
45
+ yield data
46
+
47
+
48
+
49
+ def sound_stream(request, sound_id):
50
+ sound = get_object_or_404(Sound, id=sound_id)
51
+ path = sound.file.path
52
+ content_type = "audio/" + path.split('.')[-1]
53
+
54
+ range_header = request.META.get("HTTP_RANGE", "").strip()
55
+ RANGE_RE = re.compile(r"bytes\s*=\s*(\d+)\s*-\s*(\d*)", re.I)
56
+ range_match = RANGE_RE.match(range_header)
57
+ size = os.path.getsize(path)
58
+
59
+ if range_match:
60
+ print(f"RANGE HEADER: {range_header}")
61
+ first_byte, last_byte = range_match.groups()
62
+ first_byte = int(first_byte) if first_byte else 0
63
+ last_byte = (
64
+ first_byte + 1024 * 1024 * 8
65
+ ) # The max volume of the response body is 8M per piece
66
+ if last_byte >= size:
67
+ last_byte = size - 1
68
+ length = last_byte - first_byte + 1
69
+ response = StreamingHttpResponse(
70
+ file_iterator(path, offset=first_byte, length=length),
71
+ status=206,
72
+ content_type=content_type,
73
+ )
74
+ response["Content-Range"] = f"bytes {first_byte}-{last_byte}/{size}"
75
+
76
+ else:
77
+ response = StreamingHttpResponse(
78
+ FileWrapper(open(path, "rb")), content_type=content_type
79
+ )
80
+ response["Accept-Ranges"] = "bytes"
81
+ return response
simo/urls.py CHANGED
@@ -55,10 +55,14 @@ for name, app in apps.app_configs.items():
55
55
  'staticfiles'
56
56
  ):
57
57
  continue
58
+
58
59
  try:
59
60
  urls = importlib.import_module('%s.auto_urls' % app.name)
60
- except ModuleNotFoundError:
61
- continue
61
+ except ModuleNotFoundError as e:
62
+ if '%s.auto_urls' % app.name not in e.msg:
63
+ raise e
64
+ else:
65
+ continue
62
66
 
63
67
  for var_name, item in urls.__dict__.items():
64
68
  if isinstance(item, list) and var_name == 'urlpatterns':
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: simo
3
- Version: 2.7.15
3
+ Version: 2.7.16
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
@@ -3,7 +3,7 @@ simo/asgi.py,sha256=L8CUVZLM32IMzWDZ4IShdDN-m69t7oxAUeHods4-xNM,822
3
3
  simo/celeryc.py,sha256=eab7_e9rw0c__DCeoUFUh_tjAGVlulxVrk75BaJf57Q,1512
4
4
  simo/conf.py,sha256=H2BhXAV8MEDVXF8AbkaLSfR4ULd-9_bS4bnhE5sE5fg,112
5
5
  simo/settings.py,sha256=CjHxwQvyljm0qVOCHvi6gdMweDzEqAedXTmGMJ6rE1A,7009
6
- simo/urls.py,sha256=fRmAsNQ_pzFloimLmxNeDcR6hHRJ3rOoZ3kGy8zOQ_A,2402
6
+ simo/urls.py,sha256=d8g-wN0Xr2PVIV8RZl_h_PMN9KGZNIE9to2hQj1p1TU,2497
7
7
  simo/__pycache__/__init__.cpython-312.pyc,sha256=a12_Zr7kC5DXzcFxA5eMu-TiSU5xbdF5cdKq-gwc3x0,159
8
8
  simo/__pycache__/__init__.cpython-38.pyc,sha256=j81de0BqHMr6bs0C7cuYrXl7HwtK_vv8hDEtAdSwDJc,153
9
9
  simo/__pycache__/asgi.cpython-312.pyc,sha256=3TFbweG4IzHv2br0bONji5wjd26pUvs1r_7fGXVBUVg,1263
@@ -14,14 +14,14 @@ simo/__pycache__/conf.cpython-312.pyc,sha256=q63YJWqaaaQLz3qXW8clENjvH1zUfY_k34_
14
14
  simo/__pycache__/conf.cpython-38.pyc,sha256=MYP2yk3ULxiYwZsZR6tCLjKnU-z03A3avzQzIn66y3k,273
15
15
  simo/__pycache__/settings.cpython-312.pyc,sha256=PE2CUT2j6Uj3xB5LpEEyw8q9MQLsoZPRgcOsUDh9y8o,6688
16
16
  simo/__pycache__/settings.cpython-38.pyc,sha256=4w3ds3D9S78zbsovXsXC05PYBAafDrtsOhX14FT0YyE,6149
17
- simo/__pycache__/urls.cpython-312.pyc,sha256=PsVV-CLy2lFedrt0F3xxlhQnno7ji4pUWMo7lp23ArQ,3577
17
+ simo/__pycache__/urls.cpython-312.pyc,sha256=mIg_YD7zgjmIzfWzpGpikMqanGKP2O-iuA1ixqQngnc,3689
18
18
  simo/__pycache__/urls.cpython-38.pyc,sha256=u0x6EqT8S1YfDOSPgbI8Kf-RDlveY9OV-EDXMYKAQ7w,2125
19
19
  simo/__pycache__/wsgi.cpython-38.pyc,sha256=TpRxO7VM_ql31hbKphVdanydC5RI1nHB4l0QA2pdWxo,322
20
20
  simo/automation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
21
  simo/automation/app_widgets.py,sha256=gaqImMZjuMHm7nIb9a4D-Y3qipz_WhSPAHXcwGx4Uzs,199
22
22
  simo/automation/controllers.py,sha256=Ow8xG9hkdyMRZbrNPX5uZloYM9jZqa9mgCh4k6FeoJw,11492
23
23
  simo/automation/forms.py,sha256=UWnkxw8pILPK0smRPTo4SLgsZl78zOySx7JIc30Bgtk,10228
24
- simo/automation/gateways.py,sha256=14yR5RH2JAaLUhB2xDPcrN4_XdEmvXKuOnGyueJsbTk,15487
24
+ simo/automation/gateways.py,sha256=Hej6v2K6MM8ABh96nIvCvRy5PxggJuDhoJ5WtRBlClA,15672
25
25
  simo/automation/helpers.py,sha256=iP-fxxB8HsFQy3k2CjFubu86aMqvWgmh-p24DiyOrek,4330
26
26
  simo/automation/models.py,sha256=l45FHgeKGsfpLtd1X1PVFpIjB5JI4BlvKkodpcxm6aE,927
27
27
  simo/automation/serializers.py,sha256=PjyFrjdPK1mBsgbNhyqMi9SWzcymqTa742ipy0LhAN4,1996
@@ -34,7 +34,7 @@ simo/automation/__pycache__/controllers.cpython-312.pyc,sha256=w2yAFvk-Fh1MZha7J
34
34
  simo/automation/__pycache__/controllers.cpython-38.pyc,sha256=CL-0Tq9B4-E36fYfWT1XEBTq1dkq1W8003f6MrBnQU0,8391
35
35
  simo/automation/__pycache__/forms.cpython-312.pyc,sha256=63rU0rWZk-Rz5qoMZiXl743WPc9NVm5d8bSd8w52T4E,12347
36
36
  simo/automation/__pycache__/forms.cpython-38.pyc,sha256=cpA5hA2Iz3JsPC0Dq01ki1I7S9c5DKRcXveHApI1dJo,7772
37
- simo/automation/__pycache__/gateways.cpython-312.pyc,sha256=vLYxF1tVffLstbO4ip_5XsXrjCNvDcPWdRhh2CsIXs8,21827
37
+ simo/automation/__pycache__/gateways.cpython-312.pyc,sha256=UMjaV_-JQRHC9I-tVnDxSZcRnvoOG59ZyJnkoLXBHfg,22129
38
38
  simo/automation/__pycache__/gateways.cpython-38.pyc,sha256=nHujqChMCqqxHbZezP3MisavjKDhczqzFGurO10h-lc,11113
39
39
  simo/automation/__pycache__/helpers.cpython-312.pyc,sha256=aDFtzBE72szi4gzVxK_NiAEC__wCmdztw0UKu2lVU58,5853
40
40
  simo/automation/__pycache__/helpers.cpython-38.pyc,sha256=fNjSyn4Mfq7-JQx-bdsnj-rSxgu20dVJ9-5ZEMT6yiM,3627
@@ -312,6 +312,7 @@ simo/core/migrations/0045_alter_instance_device_report_history_days_and_more.py,
312
312
  simo/core/migrations/0046_component_value_translation_alter_gateway_type.py,sha256=SUybDSLRCGevVS9mCQGvozO8LA43tKze3R5rCf1TQ4E,1282
313
313
  simo/core/migrations/0047_alter_component_value_translation.py,sha256=3GAgUBkZKUwWJMbDJxJJLBicC8eKkQ2ZJA__-xYpBCo,733
314
314
  simo/core/migrations/0048_publicfile_privatefile.py,sha256=3NAP6f1ep66-CHJ7olSYOnXXeN7fTAg76lEjTJE1rL8,1601
315
+ simo/core/migrations/0049_alter_gateway_type.py,sha256=Hs6-idTvebddqrFOs-HQY3m-HAq8e2ko2gMuf7TZBUo,797
315
316
  simo/core/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
316
317
  simo/core/migrations/__pycache__/0001_initial.cpython-312.pyc,sha256=2qtefpMLlqb1zcvIAQcW1XHwAqa0777LybQncwIoTwM,10893
317
318
  simo/core/migrations/__pycache__/0001_initial.cpython-38.pyc,sha256=w6GiBXVxWj30Bg4Sn_pFVeA041d-pCrkaq8mR3KuF70,5381
@@ -409,6 +410,7 @@ simo/core/migrations/__pycache__/0047_alter_component_value_translation.cpython-
409
410
  simo/core/migrations/__pycache__/0047_alter_component_value_translation.cpython-38.pyc,sha256=ucmaVOjI3q78vBTiEC7Aq76CklWq3fsC24EiH-mIzGc,961
410
411
  simo/core/migrations/__pycache__/0048_publicfile_privatefile.cpython-312.pyc,sha256=R-e0LlBMXd48iYwYPx1FWBXMgkojbVTen0yzWAZ0qto,2331
411
412
  simo/core/migrations/__pycache__/0048_publicfile_privatefile.cpython-38.pyc,sha256=Xtycj-4jvkB8UjVdgZZRzTgYl68Cr-Jj9WqCAho1Ggk,1346
413
+ simo/core/migrations/__pycache__/0049_alter_gateway_type.cpython-312.pyc,sha256=yRCnPOqiDfsxwhpsZpFetdRU-wwPmlcsko0naC9vs7w,1172
412
414
  simo/core/migrations/__pycache__/__init__.cpython-312.pyc,sha256=BhHR8x2b6O7PeccXle2QiefejTa-RussOoAENjeUKnE,175
413
415
  simo/core/migrations/__pycache__/__init__.cpython-38.pyc,sha256=VZmDQ57BTcebuM0KMhjiTOabgWZCBxQmSJzWZos9SO8,169
414
416
  simo/core/static/ansi_styles.css,sha256=4ieJGrjZPKyPSago9FdB_gflHoGE1vxCHi8qVn5tY-Y,37352
@@ -10589,9 +10591,9 @@ simo/fleet/templates/fleet/controllers_info/ENS160AirQualitySensor.md,sha256=3LS
10589
10591
  simo/generic/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10590
10592
  simo/generic/app_widgets.py,sha256=y8W3jR76Hh26O9pPQyg2SophMbYIOtAWD33MPKbB8Mg,856
10591
10593
  simo/generic/base_types.py,sha256=u3SlfpNYaCwkVBwomWgso4ODzL71ay9MhiAW-bxgnDU,341
10592
- simo/generic/controllers.py,sha256=gqL1Evt-8Cq8MeHG-eV8aCtQ4St_gylE52wnXwOQUWU,46285
10593
- simo/generic/forms.py,sha256=cGHqhCsCO3fe7Y1f_1vERA8vecXw5TjrVoacTG4AC1Q,25856
10594
- simo/generic/gateways.py,sha256=04FKHgg3_cWjhu-bxexgRFpKRavW6_StgugFcDYwkiQ,17471
10594
+ simo/generic/controllers.py,sha256=HDhf-bC6WATWzHFFEndap4z213n0LgtBhVG1lfMxzMU,46597
10595
+ simo/generic/forms.py,sha256=N9Jv4-e2IroSAeXXrpdpThQnH2sw5ADh08nFtdOK9Ds,26035
10596
+ simo/generic/gateways.py,sha256=bq01qX2Q62B47yHmTKdCfQE8tcwmI70jvJko-pIyQag,17534
10595
10597
  simo/generic/models.py,sha256=Adq7ipWK-renxJlNW-SZnAq2oGEOwKx8EdUWaKnfcVQ,7597
10596
10598
  simo/generic/routing.py,sha256=elQVZmgnPiieEuti4sJ7zITk1hlRxpgbotcutJJgC60,228
10597
10599
  simo/generic/socket_consumers.py,sha256=pyiqzfGxSKBNqfrfEJ_kCU0UbSC28XnvDn6QjKkbqyY,1767
@@ -10601,11 +10603,11 @@ simo/generic/__pycache__/app_widgets.cpython-312.pyc,sha256=ywoKk91YSEZxpyt9haG5
10601
10603
  simo/generic/__pycache__/app_widgets.cpython-38.pyc,sha256=D9b13pbMlirgHmjDnQhfLIDGSVINoSouHb4SWOeCRrs,1642
10602
10604
  simo/generic/__pycache__/base_types.cpython-312.pyc,sha256=h8Mwu49i-zmwTbL33JaLJfRDGOgkQh2_VqrfzEc4UQ4,616
10603
10605
  simo/generic/__pycache__/base_types.cpython-38.pyc,sha256=aV5NdIuvXR-ItKpI__MwcyPZHD6Z882TFdgYkPCkr1I,493
10604
- simo/generic/__pycache__/controllers.cpython-312.pyc,sha256=eoZ20mKplThgrnH0VU0q2xpKmhY6W89eGavU_l112Hg,52542
10606
+ simo/generic/__pycache__/controllers.cpython-312.pyc,sha256=g5fjTUYYNFGR7Sv-hoES7Vfofc7bxMk0Ef6nGNhvgNM,53145
10605
10607
  simo/generic/__pycache__/controllers.cpython-38.pyc,sha256=jJjwKVaDYyazrRGNjUFoY74nr_jX_DEnsC9KjyxZCgc,30427
10606
- simo/generic/__pycache__/forms.cpython-312.pyc,sha256=IpJVFybWvHlaOALHXTGyrYLTSXkjRJyYZ7AAM4dDeBg,34307
10608
+ simo/generic/__pycache__/forms.cpython-312.pyc,sha256=NtOAjTxXh0vaYNdUY9PVJy1Q9Bm_HiFX6oXxW5yQ62Y,34676
10607
10609
  simo/generic/__pycache__/forms.cpython-38.pyc,sha256=k8lz3taXdWAg5P9jcnw66mWH51pCc4SOsg32kVEtBCg,19416
10608
- simo/generic/__pycache__/gateways.cpython-312.pyc,sha256=v4SJA_S0un_0oMWbqnGnSVX-xBUL9ZW2f4VxeuQtwAM,23957
10610
+ simo/generic/__pycache__/gateways.cpython-312.pyc,sha256=bq-k85HBSfg_EHtDFQmo1BMBDjgPqP0lfw5BEnP1vB4,21672
10609
10611
  simo/generic/__pycache__/gateways.cpython-38.pyc,sha256=GIeMT51oZU2OCFD4eUDFdSRRYE0Qf14AcOr_gdUqG94,12705
10610
10612
  simo/generic/__pycache__/models.cpython-312.pyc,sha256=xriUzjkaM2Y4mT3jo2OPK-XGBroBBSFJfLqK0jMA4MA,10200
10611
10613
  simo/generic/__pycache__/models.cpython-38.pyc,sha256=MZpum7syAFxuulf47K7gtUlJJ7xRD-IBUBAwUM1ZRnw,5825
@@ -10648,43 +10650,44 @@ simo/generic/templates/admin/controller_widgets/weather.html,sha256=84SESQBhhzNU
10648
10650
  simo/generic/templates/generic/controllers_info/dummy.md,sha256=DcdkpYXpK7sroINukZZPUQs9uekN9kkE7p5hfnArgFo,147
10649
10651
  simo/generic/templates/generic/controllers_info/stateselect.md,sha256=T0w3vJg02W3RMSsljN1EPRnkVaeRW5acSZaSq9FYvZw,135
10650
10652
  simo/multimedia/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10651
- simo/multimedia/admin.py,sha256=S7WYYTwsd73WVU60MDVsheyRH9bDnsMZezV_kPJhYZ0,1031
10653
+ simo/multimedia/admin.py,sha256=gK6YuWVao52srVPVpR8R0muJcEvM7aMKd8F9AayNO2I,1047
10652
10654
  simo/multimedia/api.py,sha256=mZ5BTggWdc_kL8P70JGC3rTCiZKPnxWYoyNcAQkFnX4,285
10653
10655
  simo/multimedia/app_widgets.py,sha256=_M-HsUzlW1V2LxVw5SUH8Og9b4u0e179gCp-sFhfoPM,331
10654
- simo/multimedia/auto_urls.py,sha256=9FUwlifvAM-AJbAVK6ZCjsQdAZfREuf_sf-kSYjMu7I,204
10656
+ simo/multimedia/auto_urls.py,sha256=dF9B1OZX_xpYbWgd3uR4IVeMIMMUS-zXqVjozaEfrUQ,310
10655
10657
  simo/multimedia/base_types.py,sha256=dAP7_uh_b3A03yXBJZyQdRFucKIro4_RkIZ5yOaWXVE,151
10656
- simo/multimedia/controllers.py,sha256=4z_s0YZmheZcTKir_NFFR3XtVFV7-egPndNrwNLlf7w,3561
10658
+ simo/multimedia/controllers.py,sha256=i9Q9j8WHuLwJBXZ2E0gQOLNu47QuH34MVEDW9mpGl5Q,3650
10657
10659
  simo/multimedia/forms.py,sha256=oMCVUXRNiESrY3w_uBLRRgjMjx8BrmNeVglzorA9QtY,239
10658
- simo/multimedia/models.py,sha256=qUQaADxFegVMbcEdjyP72UCq5kkqJ5ZSe2lu9qyoom8,1169
10660
+ simo/multimedia/models.py,sha256=Q5Fi8YARkimQ3uyKAAlv50XuqXzzOGCOaXha4G4IQ3g,1305
10659
10661
  simo/multimedia/serializers.py,sha256=9DRGsJVJLKdqmOLiVHMY06bTTYxpABhDy1JB_klzsBw,383
10660
- simo/multimedia/views.py,sha256=oFhtJQJT5-XRtzoIKo7SWqLydCB9AWzcj2a-kQ5P7dw,558
10662
+ simo/multimedia/views.py,sha256=HWj4FYPRqCUc8l40lqdQAecHkNe0VhbO7kN9Bu7kNpc,2505
10661
10663
  simo/multimedia/__pycache__/__init__.cpython-312.pyc,sha256=NGPM-a2cNEN2nd_L7dW3cRwes3B6ZnsH7vx3mSD3OUg,170
10662
10664
  simo/multimedia/__pycache__/__init__.cpython-38.pyc,sha256=BOLHOifu6r_MuWjddGcQVsYkqYlkmHvPQIcx3selLIk,164
10663
- simo/multimedia/__pycache__/admin.cpython-312.pyc,sha256=OD_xYZKw0Ujfpbd2Yob7sX05kEapECSSp3J6qEe0pXs,1813
10665
+ simo/multimedia/__pycache__/admin.cpython-312.pyc,sha256=emz0M7DQAsBzRHIzz9RjwEVn3EEA_lRTNPBYCELFbdU,1820
10664
10666
  simo/multimedia/__pycache__/admin.cpython-38.pyc,sha256=xIax_AtzQlJiQmEBG5xuhJz2-Ztf40e26AQs5oBISfw,1309
10665
10667
  simo/multimedia/__pycache__/api.cpython-312.pyc,sha256=H4g5L2s2btP8AJOe9s95X3AnElEj6GOM1IYEHh1_lTI,727
10666
10668
  simo/multimedia/__pycache__/api.cpython-38.pyc,sha256=lFGEB74vgyEM98B42wwcN9WvH8FAupIiSY0SwEBd5Dw,605
10667
10669
  simo/multimedia/__pycache__/app_widgets.cpython-312.pyc,sha256=mkhOltpNGI8pnKkr3G06yjPwhtpQNbxF-7DzckP39_Y,809
10668
10670
  simo/multimedia/__pycache__/app_widgets.cpython-38.pyc,sha256=ZkTAF-us5U5mjc8fsT0z8lKA-6eRL4isl3tpqUqjlus,735
10669
- simo/multimedia/__pycache__/auto_urls.cpython-312.pyc,sha256=oZX7sI5E1Xw7m7je8jzfkMZWFg2_ZwRg7DwTlZLASxc,427
10671
+ simo/multimedia/__pycache__/auto_urls.cpython-312.pyc,sha256=dOonI_zazvAP6tVJboqrq-f8Z6YevELNhAVQWzy7PaI,552
10670
10672
  simo/multimedia/__pycache__/auto_urls.cpython-38.pyc,sha256=JNI3pZNFC3NoJV4cxsy-Oe6Vc6TA9fpPU63hjlXWINE,357
10671
10673
  simo/multimedia/__pycache__/base_types.cpython-312.pyc,sha256=jxgtUvy_XYES0ZaUnHylyDcG2dS1LlFqdJyenhUQ8k4,368
10672
10674
  simo/multimedia/__pycache__/base_types.cpython-38.pyc,sha256=c4nmNziLs4RB3MAnxltn3W5XNW6PM5_vK_mm3Yvy42Y,324
10673
- simo/multimedia/__pycache__/controllers.cpython-312.pyc,sha256=Z4WYtzIeTwXDpbFQKJKy9CCHf0QM4fv8icQ_R9b9D_4,6334
10675
+ simo/multimedia/__pycache__/controllers.cpython-312.pyc,sha256=hEaVHg_wfOasxK5rgvZdIRnajmb2idwmea9h9t8FtfU,5943
10674
10676
  simo/multimedia/__pycache__/controllers.cpython-38.pyc,sha256=sbWvA-ro93qWxV6Qk8Pv_WPsq91tqlnFPIvCeQ0tvLA,4997
10675
10677
  simo/multimedia/__pycache__/forms.cpython-312.pyc,sha256=lMdfAkCN2zOgLfPAOBQzOIlcReFpL1BdsjwQ_XXaOfk,774
10676
10678
  simo/multimedia/__pycache__/forms.cpython-38.pyc,sha256=99h7Yj2jim3QOrqej00wiPufrCF3F--RoYvwa6lzhPI,697
10677
- simo/multimedia/__pycache__/models.cpython-312.pyc,sha256=zVjtp8EOic9Vh03GYiD9lixGlpEqp3trF8mEFLs3o9w,2353
10679
+ simo/multimedia/__pycache__/models.cpython-312.pyc,sha256=FhSOj_gUPJhLBYuIu1u7hDV0cNScRBO2xoMOYj4ehjA,2630
10678
10680
  simo/multimedia/__pycache__/models.cpython-38.pyc,sha256=LmQ_XMA--wvLq11xYp24nU-y-kbx17iEZ10y9twCMJ0,1662
10679
10681
  simo/multimedia/__pycache__/serializers.cpython-312.pyc,sha256=XaavYbkW-tNPEkZyjWLW8gaqRwimECPTcuON213QBVw,1118
10680
10682
  simo/multimedia/__pycache__/serializers.cpython-38.pyc,sha256=n86txYSrkmN0Xlrr8dMwKSY7rEzMc1iovepCZi_Fcw8,886
10681
- simo/multimedia/__pycache__/views.cpython-312.pyc,sha256=Cz6Y5Py-_of2-kyTMcEQmtidyGv2BrKZCuu0vZGZ7eM,1407
10683
+ simo/multimedia/__pycache__/views.cpython-312.pyc,sha256=OKgt7c7kbhjCqZ97cZTir1g8drp2c6v69KxF6ywi9t4,3870
10682
10684
  simo/multimedia/__pycache__/views.cpython-38.pyc,sha256=yq5Hi2HrbSNr8MP4uUZO7_GMeVgqg21R6yFe85zNNhM,899
10683
10685
  simo/multimedia/migrations/0001_initial.py,sha256=YxTbmI-aF9OZUkFPnCNqV3t3r5nSJg59pKcTvm-ZYbU,610
10684
10686
  simo/multimedia/migrations/0002_sound_length.py,sha256=XpPemInQwL1QsiqhUPkdBPBYI2E5gDPyC_1vhyImq5M,471
10685
10687
  simo/multimedia/migrations/0003_alter_sound_length.py,sha256=tt1OJVkrCo6Ybws1mGobdvTP5o68P3-cHZdGG_PeZwU,442
10686
10688
  simo/multimedia/migrations/0004_auto_20231023_1055.py,sha256=ilH1o_wxkK5QDjcPEEDhoHWx2GcjZWLG6XjlUfQRmhI,718
10687
10689
  simo/multimedia/migrations/0005_remove_sound_slug_sound_date_uploaded.py,sha256=SzkgIhCIEVMSmxVwGHQfggdsX4wWybvskqVx-vVp2Qs,602
10690
+ simo/multimedia/migrations/0006_remove_sound_length_sound_duration.py,sha256=aVw9gi5FfVuH_U0acnPEsl48DEPSiO5r8EK0UI1b4-M,572
10688
10691
  simo/multimedia/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10689
10692
  simo/multimedia/migrations/__pycache__/0001_initial.cpython-312.pyc,sha256=2glBfOpmU6Sp3RExZ41jiwzGPWl8JvaUtPBWYUF7gWg,1063
10690
10693
  simo/multimedia/migrations/__pycache__/0001_initial.cpython-38.pyc,sha256=slsqUkj8A11Oj5bYJ1s5dS60Qx4H9k_oRZ3HaAhuho4,760
@@ -10696,6 +10699,7 @@ simo/multimedia/migrations/__pycache__/0004_auto_20231023_1055.cpython-312.pyc,s
10696
10699
  simo/multimedia/migrations/__pycache__/0004_auto_20231023_1055.cpython-38.pyc,sha256=tX6x1gNeVcUHcCFvisP9Z2jPBE029TJG632fvtvUjV8,892
10697
10700
  simo/multimedia/migrations/__pycache__/0005_remove_sound_slug_sound_date_uploaded.cpython-312.pyc,sha256=gGYLNmOVIrh2TjsxjVRbcLXQI46mwV_0e14mEVHWITo,1066
10698
10701
  simo/multimedia/migrations/__pycache__/0005_remove_sound_slug_sound_date_uploaded.cpython-38.pyc,sha256=vpu4EfpVp5rYRKYEVn4vDylgpeasD31ZV62sNR_HXhY,801
10702
+ simo/multimedia/migrations/__pycache__/0006_remove_sound_length_sound_duration.cpython-312.pyc,sha256=kn0T_kxdq5dFspvXcDnp_i0ilnF7ssZYUZZLh5E9DqA,962
10699
10703
  simo/multimedia/migrations/__pycache__/__init__.cpython-312.pyc,sha256=63PjzEbaT7KQ3tQSmpMvM3ry31Eu40r7Zq8C4y2z6FY,181
10700
10704
  simo/multimedia/migrations/__pycache__/__init__.cpython-38.pyc,sha256=mCgSiQBphL85imdWyTi9-4zBDYF6HfXbhB0ycSPVVuA,175
10701
10705
  simo/multimedia/templates/admin/controller_widgets/player.html,sha256=v4PFoL37-C7L6ILZI6yF5ff9iO3EnEs5kXbRayaHy0s,358
@@ -10920,9 +10924,9 @@ simo/users/templates/invitations/expired_msg.html,sha256=47DEQpj8HBSa-_TImW-5JCe
10920
10924
  simo/users/templates/invitations/expired_suggestion.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10921
10925
  simo/users/templates/invitations/taken_msg.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10922
10926
  simo/users/templates/invitations/taken_suggestion.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10923
- simo-2.7.15.dist-info/LICENSE.md,sha256=M7wm1EmMGDtwPRdg7kW4d00h1uAXjKOT3HFScYQMeiE,34916
10924
- simo-2.7.15.dist-info/METADATA,sha256=f1LcmhjxwiyTrqY9-ZFUIqD7xe7JbKaGV-I7Pboa-qU,2009
10925
- simo-2.7.15.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
10926
- simo-2.7.15.dist-info/entry_points.txt,sha256=S9PwnUYmTSW7681GKDCxUbL0leRJIaRk6fDQIKgbZBA,135
10927
- simo-2.7.15.dist-info/top_level.txt,sha256=GmS1hrAbpVqn9OWZh6UX82eIOdRLgYA82RG9fe8v4Rs,5
10928
- simo-2.7.15.dist-info/RECORD,,
10927
+ simo-2.7.16.dist-info/LICENSE.md,sha256=M7wm1EmMGDtwPRdg7kW4d00h1uAXjKOT3HFScYQMeiE,34916
10928
+ simo-2.7.16.dist-info/METADATA,sha256=uuLTNmwN-BR8ZrQ9eBFoXWOf1Roj8fqHPUdwJNlA-5k,2009
10929
+ simo-2.7.16.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
10930
+ simo-2.7.16.dist-info/entry_points.txt,sha256=S9PwnUYmTSW7681GKDCxUbL0leRJIaRk6fDQIKgbZBA,135
10931
+ simo-2.7.16.dist-info/top_level.txt,sha256=GmS1hrAbpVqn9OWZh6UX82eIOdRLgYA82RG9fe8v4Rs,5
10932
+ simo-2.7.16.dist-info/RECORD,,
File without changes