simo 3.1.5__py3-none-any.whl → 3.1.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of simo might be problematic. Click here for more details.
- simo/automation/gateways.py +17 -7
- simo/core/controllers.py +4 -1
- simo/core/events.py +15 -3
- simo/core/forms.py +93 -19
- simo/core/gateways.py +10 -2
- simo/core/management/commands/gateways_manager.py +14 -4
- simo/generic/gateways.py +25 -9
- {simo-3.1.5.dist-info → simo-3.1.7.dist-info}/METADATA +1 -1
- {simo-3.1.5.dist-info → simo-3.1.7.dist-info}/RECORD +13 -13
- {simo-3.1.5.dist-info → simo-3.1.7.dist-info}/WHEEL +0 -0
- {simo-3.1.5.dist-info → simo-3.1.7.dist-info}/entry_points.txt +0 -0
- {simo-3.1.5.dist-info → simo-3.1.7.dist-info}/licenses/LICENSE.md +0 -0
- {simo-3.1.5.dist-info → simo-3.1.7.dist-info}/top_level.txt +0 -0
simo/automation/gateways.py
CHANGED
|
@@ -311,11 +311,19 @@ class AutomationsGatewayHandler(GatesHandler, BaseObjectCommandsGatewayHandler):
|
|
|
311
311
|
|
|
312
312
|
from .controllers import Script
|
|
313
313
|
|
|
314
|
-
mqtt_client = mqtt.Client()
|
|
315
|
-
mqtt_client.username_pw_set('root', settings.SECRET_KEY)
|
|
316
|
-
mqtt_client.on_connect = self.on_mqtt_connect
|
|
317
|
-
mqtt_client.on_message = self.on_mqtt_message
|
|
318
|
-
|
|
314
|
+
self.mqtt_client = mqtt.Client()
|
|
315
|
+
self.mqtt_client.username_pw_set('root', settings.SECRET_KEY)
|
|
316
|
+
self.mqtt_client.on_connect = self.on_mqtt_connect
|
|
317
|
+
self.mqtt_client.on_message = self.on_mqtt_message
|
|
318
|
+
try:
|
|
319
|
+
self.mqtt_client.reconnect_delay_set(min_delay=1, max_delay=30)
|
|
320
|
+
except Exception:
|
|
321
|
+
pass
|
|
322
|
+
try:
|
|
323
|
+
# Avoid raising if broker restarts or is down at boot
|
|
324
|
+
self.mqtt_client.connect_async(host=settings.MQTT_HOST, port=settings.MQTT_PORT)
|
|
325
|
+
except Exception:
|
|
326
|
+
pass
|
|
319
327
|
|
|
320
328
|
# We presume that this is the only running gateway, therefore
|
|
321
329
|
# if there are any running scripts, that is not true.
|
|
@@ -337,9 +345,11 @@ class AutomationsGatewayHandler(GatesHandler, BaseObjectCommandsGatewayHandler):
|
|
|
337
345
|
self.start_script(script)
|
|
338
346
|
|
|
339
347
|
print("GATEWAY STARTED!")
|
|
348
|
+
self.mqtt_client.loop_start()
|
|
340
349
|
while not exit.is_set():
|
|
341
|
-
|
|
342
|
-
mqtt_client.
|
|
350
|
+
time.sleep(1)
|
|
351
|
+
self.mqtt_client.loop_stop()
|
|
352
|
+
self.mqtt_client.disconnect()
|
|
343
353
|
|
|
344
354
|
script_ids = [id for id in self.running_scripts.keys()]
|
|
345
355
|
for id in script_ids:
|
simo/core/controllers.py
CHANGED
|
@@ -647,7 +647,10 @@ class Button(ControllerBase):
|
|
|
647
647
|
default_value = 'up'
|
|
648
648
|
|
|
649
649
|
def _validate_val(self, value, occasion=None):
|
|
650
|
-
if value not in (
|
|
650
|
+
if value not in (
|
|
651
|
+
'down', 'up', 'hold',
|
|
652
|
+
'click', 'double-click', 'triple-click', 'quadruple-click', 'quintuple-click'
|
|
653
|
+
):
|
|
651
654
|
raise ValidationError("Bad button value!")
|
|
652
655
|
return value
|
|
653
656
|
|
simo/core/events.py
CHANGED
|
@@ -181,9 +181,21 @@ class OnChangeMixin:
|
|
|
181
181
|
self._mqtt_client.username_pw_set('root', settings.SECRET_KEY)
|
|
182
182
|
self._mqtt_client.on_connect = self.on_mqtt_connect
|
|
183
183
|
self._mqtt_client.on_message = self.on_mqtt_message
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
184
|
+
# Be gentle when broker is down or connection flaps
|
|
185
|
+
try:
|
|
186
|
+
# paho 1.x supports reconnect backoff configuration
|
|
187
|
+
self._mqtt_client.reconnect_delay_set(min_delay=1, max_delay=30)
|
|
188
|
+
except Exception:
|
|
189
|
+
pass
|
|
190
|
+
try:
|
|
191
|
+
self._mqtt_client.connect_async(
|
|
192
|
+
host=settings.MQTT_HOST, port=settings.MQTT_PORT
|
|
193
|
+
)
|
|
194
|
+
except Exception:
|
|
195
|
+
# connect_async should not normally raise; in case of programming
|
|
196
|
+
# errors keep the API surface consistent by cleaning up
|
|
197
|
+
self._mqtt_client = None
|
|
198
|
+
raise
|
|
187
199
|
self._mqtt_client.loop_start()
|
|
188
200
|
self._on_change_function = function
|
|
189
201
|
self._obj_ct_id = ContentType.objects.get_for_model(self).pk
|
simo/core/forms.py
CHANGED
|
@@ -71,16 +71,45 @@ class CategoryAdminForm(forms.ModelForm):
|
|
|
71
71
|
class ConfigFieldsMixin:
|
|
72
72
|
|
|
73
73
|
def __init__(self, *args, **kwargs):
|
|
74
|
+
"""Augment forms with dynamic controller fields and
|
|
75
|
+
persist non-model fields under component.config.
|
|
76
|
+
|
|
77
|
+
Dynamic fields are appended by controllers via
|
|
78
|
+
`controller._get_dynamic_config_fields()` and are excluded from
|
|
79
|
+
automatic component.config persistence. Controllers may handle
|
|
80
|
+
them in `_apply_dynamic_config()` during save.
|
|
81
|
+
"""
|
|
74
82
|
super().__init__(*args, **kwargs)
|
|
83
|
+
|
|
84
|
+
# Inject dynamic fields from controller (if any)
|
|
85
|
+
self._dynamic_fields = []
|
|
86
|
+
controller = getattr(self, 'controller', None)
|
|
87
|
+
if controller and hasattr(controller, '_get_dynamic_config_fields'):
|
|
88
|
+
try:
|
|
89
|
+
dyn_fields = controller._get_dynamic_config_fields() or {}
|
|
90
|
+
if isinstance(dyn_fields, dict):
|
|
91
|
+
for fname, field in dyn_fields.items():
|
|
92
|
+
if fname in self.fields:
|
|
93
|
+
continue
|
|
94
|
+
self.fields[fname] = field
|
|
95
|
+
self._dynamic_fields.append(fname)
|
|
96
|
+
except Exception:
|
|
97
|
+
# Never break the form if controller fails here
|
|
98
|
+
pass
|
|
99
|
+
|
|
100
|
+
# Build config-backed field list (exclude model fields and dynamic fields)
|
|
75
101
|
self.model_fields = [
|
|
76
102
|
f.name for f in Component._meta.fields
|
|
77
103
|
] + ['slaves', ]
|
|
78
104
|
self.config_fields = []
|
|
79
|
-
for field_name
|
|
105
|
+
for field_name in list(self.fields.keys()):
|
|
80
106
|
if field_name in self.model_fields:
|
|
81
107
|
continue
|
|
108
|
+
if field_name in self._dynamic_fields:
|
|
109
|
+
continue
|
|
82
110
|
self.config_fields.append(field_name)
|
|
83
111
|
|
|
112
|
+
# Initialize config-backed fields from instance.config
|
|
84
113
|
for field_name in self.config_fields:
|
|
85
114
|
if field_name not in self.instance.config:
|
|
86
115
|
continue
|
|
@@ -106,38 +135,46 @@ class ConfigFieldsMixin:
|
|
|
106
135
|
|
|
107
136
|
|
|
108
137
|
def save(self, commit=True):
|
|
138
|
+
# Write config-backed fields under component.config
|
|
109
139
|
for field_name in self.config_fields:
|
|
110
140
|
# support for partial forms
|
|
111
141
|
if field_name not in self.cleaned_data:
|
|
112
142
|
continue
|
|
113
143
|
if isinstance(self.cleaned_data[field_name], models.Model):
|
|
114
|
-
self.instance.config[field_name] =
|
|
115
|
-
self.cleaned_data[field_name].pk
|
|
144
|
+
self.instance.config[field_name] = self.cleaned_data[field_name].pk
|
|
116
145
|
elif isinstance(self.cleaned_data[field_name], models.QuerySet):
|
|
117
|
-
self.instance.config[field_name] = [
|
|
118
|
-
obj.pk for obj in self.cleaned_data[field_name]
|
|
119
|
-
]
|
|
146
|
+
self.instance.config[field_name] = [obj.pk for obj in self.cleaned_data[field_name]]
|
|
120
147
|
else:
|
|
121
148
|
try:
|
|
122
|
-
self.instance.config[field_name] =
|
|
123
|
-
|
|
124
|
-
except:
|
|
149
|
+
self.instance.config[field_name] = json.loads(json.dumps(self.cleaned_data[field_name]))
|
|
150
|
+
except Exception:
|
|
125
151
|
continue
|
|
126
152
|
|
|
153
|
+
# Save component
|
|
127
154
|
if commit:
|
|
128
155
|
from simo.users.utils import get_current_user
|
|
129
156
|
actor = get_current_user()
|
|
130
|
-
if
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
157
|
+
if actor:
|
|
158
|
+
if self.instance.pk:
|
|
159
|
+
verb = 'modified'
|
|
160
|
+
else:
|
|
161
|
+
verb = 'created'
|
|
162
|
+
action.send(
|
|
163
|
+
actor, target=self.instance, verb=verb,
|
|
164
|
+
instance_id=self.instance.zone.instance.id,
|
|
165
|
+
action_type='management_event'
|
|
166
|
+
)
|
|
167
|
+
result = super().save(commit)
|
|
139
168
|
|
|
140
|
-
|
|
169
|
+
# Apply dynamic settings via controller hook
|
|
170
|
+
controller = getattr(self, 'controller', None)
|
|
171
|
+
if controller and hasattr(controller, '_apply_dynamic_config'):
|
|
172
|
+
try:
|
|
173
|
+
controller._apply_dynamic_config(self.cleaned_data)
|
|
174
|
+
except Exception:
|
|
175
|
+
pass
|
|
176
|
+
|
|
177
|
+
return result
|
|
141
178
|
|
|
142
179
|
|
|
143
180
|
class BaseGatewayForm(ConfigFieldsMixin, forms.ModelForm):
|
|
@@ -329,10 +366,24 @@ class ComponentAdminForm(forms.ModelForm):
|
|
|
329
366
|
base_fields.append('category')
|
|
330
367
|
base_fields.append('show_in_app')
|
|
331
368
|
|
|
369
|
+
# Include statically declared fields first
|
|
332
370
|
for field_name in cls.declared_fields:
|
|
333
371
|
if field_name not in main_fields:
|
|
334
372
|
base_fields.append(field_name)
|
|
335
373
|
|
|
374
|
+
# If editing an existing object, include any dynamic fields
|
|
375
|
+
# that the form instance adds at runtime.
|
|
376
|
+
if obj is not None:
|
|
377
|
+
try:
|
|
378
|
+
tmp_form = cls(request=request, instance=obj, controller_uid=obj.controller_uid)
|
|
379
|
+
for fname in tmp_form.fields.keys():
|
|
380
|
+
if fname in base_fields or fname in main_fields:
|
|
381
|
+
continue
|
|
382
|
+
base_fields.append(fname)
|
|
383
|
+
except Exception:
|
|
384
|
+
# Ignore dynamic field detection issues
|
|
385
|
+
pass
|
|
386
|
+
|
|
336
387
|
base_fields.append('control')
|
|
337
388
|
base_fields.append('notes')
|
|
338
389
|
|
|
@@ -389,6 +440,29 @@ class ComponentAdminForm(forms.ModelForm):
|
|
|
389
440
|
|
|
390
441
|
|
|
391
442
|
class BaseComponentForm(ConfigFieldsMixin, ComponentAdminForm):
|
|
443
|
+
"""Base form for all components.
|
|
444
|
+
|
|
445
|
+
Dynamic controller-backed fields
|
|
446
|
+
--------------------------------
|
|
447
|
+
Controllers can expose device-scoped, dynamic options by implementing
|
|
448
|
+
the following optional hooks on the controller instance:
|
|
449
|
+
|
|
450
|
+
- _get_dynamic_config_fields(self) -> dict[str, django.forms.Field]
|
|
451
|
+
Return a mapping of additional form fields to append at runtime.
|
|
452
|
+
These fields are not persisted automatically to component.config.
|
|
453
|
+
Set each field's `initial` to reflect the current device value.
|
|
454
|
+
|
|
455
|
+
- _apply_dynamic_config(self, form_data: dict) -> None
|
|
456
|
+
Called after the form is saved. Receives the full cleaned form
|
|
457
|
+
data (including dynamic fields). Use this to apply device options
|
|
458
|
+
(e.g. send commands to a gateway or write to component.config if
|
|
459
|
+
your integration needs to persist something custom).
|
|
460
|
+
|
|
461
|
+
Notes
|
|
462
|
+
- Dynamic fields are excluded from `basic_fields`, so only higher-level
|
|
463
|
+
users (instance superusers/masters) will see and edit them.
|
|
464
|
+
- All existing static fields and behavior remain unchanged.
|
|
465
|
+
"""
|
|
392
466
|
pass
|
|
393
467
|
|
|
394
468
|
|
simo/core/gateways.py
CHANGED
|
@@ -63,6 +63,10 @@ class BaseObjectCommandsGatewayHandler(BaseGatewayHandler):
|
|
|
63
63
|
self.mqtt_client.username_pw_set('root', settings.SECRET_KEY)
|
|
64
64
|
self.mqtt_client.on_connect = self._on_mqtt_connect
|
|
65
65
|
self.mqtt_client.on_message = self._on_mqtt_message
|
|
66
|
+
try:
|
|
67
|
+
self.mqtt_client.reconnect_delay_set(min_delay=1, max_delay=30)
|
|
68
|
+
except Exception:
|
|
69
|
+
pass
|
|
66
70
|
|
|
67
71
|
|
|
68
72
|
def run(self, exit):
|
|
@@ -74,7 +78,12 @@ class BaseObjectCommandsGatewayHandler(BaseGatewayHandler):
|
|
|
74
78
|
target=self._run_periodic_task, args=(self.exit, task, period), daemon=True
|
|
75
79
|
).start()
|
|
76
80
|
|
|
77
|
-
|
|
81
|
+
# Use async connect so we don't crash if broker is temporarily down
|
|
82
|
+
try:
|
|
83
|
+
self.mqtt_client.connect_async(host=settings.MQTT_HOST, port=settings.MQTT_PORT)
|
|
84
|
+
except Exception:
|
|
85
|
+
# connect_async shouldn't raise for normal scenarios; ignore just in case
|
|
86
|
+
pass
|
|
78
87
|
self.mqtt_client.loop_start()
|
|
79
88
|
|
|
80
89
|
while not self.exit.is_set():
|
|
@@ -137,4 +146,3 @@ class BaseObjectCommandsGatewayHandler(BaseGatewayHandler):
|
|
|
137
146
|
self.perform_value_send(component, val)
|
|
138
147
|
except Exception as e:
|
|
139
148
|
self.logger.error(e, exc_info=True)
|
|
140
|
-
|
|
@@ -117,18 +117,28 @@ class GatewaysManager:
|
|
|
117
117
|
self.mqtt_client.username_pw_set('root', settings.SECRET_KEY)
|
|
118
118
|
self.mqtt_client.on_connect = self.on_mqtt_connect
|
|
119
119
|
self.mqtt_client.on_message = self.on_mqtt_message
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
120
|
+
try:
|
|
121
|
+
self.mqtt_client.reconnect_delay_set(min_delay=1, max_delay=30)
|
|
122
|
+
except Exception:
|
|
123
|
+
pass
|
|
124
|
+
try:
|
|
125
|
+
self.mqtt_client.connect_async(
|
|
126
|
+
host=settings.MQTT_HOST, port=settings.MQTT_PORT
|
|
127
|
+
)
|
|
128
|
+
except Exception:
|
|
129
|
+
pass
|
|
123
130
|
|
|
131
|
+
self.mqtt_client.loop_start()
|
|
124
132
|
while not self.exit_event.is_set():
|
|
125
|
-
|
|
133
|
+
time.sleep(1)
|
|
126
134
|
|
|
127
135
|
ids_to_stop = [id for id in self.running_gateways.keys()]
|
|
128
136
|
for id in ids_to_stop:
|
|
129
137
|
self.stop_gateway(Gateway(id=id))
|
|
130
138
|
while self.running_gateways.keys():
|
|
131
139
|
time.sleep(0.3)
|
|
140
|
+
self.mqtt_client.loop_stop()
|
|
141
|
+
self.mqtt_client.disconnect()
|
|
132
142
|
close_old_connections()
|
|
133
143
|
print("-------------Gateways Manager STOPPED.------------------")
|
|
134
144
|
return sys.exit()
|
simo/generic/gateways.py
CHANGED
|
@@ -297,11 +297,19 @@ class GenericGatewayHandler(
|
|
|
297
297
|
|
|
298
298
|
from simo.generic.controllers import IPCamera
|
|
299
299
|
|
|
300
|
-
|
|
301
|
-
mqtt_client.
|
|
302
|
-
mqtt_client.
|
|
303
|
-
mqtt_client.
|
|
304
|
-
mqtt_client.
|
|
300
|
+
# Use non-blocking MQTT loop to avoid busy-spin when broker is down
|
|
301
|
+
self.mqtt_client = mqtt.Client()
|
|
302
|
+
self.mqtt_client.username_pw_set('root', settings.SECRET_KEY)
|
|
303
|
+
self.mqtt_client.on_connect = self.on_mqtt_connect
|
|
304
|
+
self.mqtt_client.on_message = self.on_mqtt_message
|
|
305
|
+
try:
|
|
306
|
+
self.mqtt_client.reconnect_delay_set(min_delay=1, max_delay=30)
|
|
307
|
+
except Exception:
|
|
308
|
+
pass
|
|
309
|
+
try:
|
|
310
|
+
self.mqtt_client.connect_async(host=settings.MQTT_HOST, port=settings.MQTT_PORT)
|
|
311
|
+
except Exception:
|
|
312
|
+
pass
|
|
305
313
|
|
|
306
314
|
for cam in Component.objects.filter(
|
|
307
315
|
controller_uid=IPCamera.uid
|
|
@@ -315,9 +323,11 @@ class GenericGatewayHandler(
|
|
|
315
323
|
).start()
|
|
316
324
|
|
|
317
325
|
print("GATEWAY STARTED!")
|
|
326
|
+
self.mqtt_client.loop_start()
|
|
318
327
|
while not exit.is_set():
|
|
319
|
-
|
|
320
|
-
mqtt_client.
|
|
328
|
+
time.sleep(1)
|
|
329
|
+
self.mqtt_client.loop_stop()
|
|
330
|
+
self.mqtt_client.disconnect()
|
|
321
331
|
|
|
322
332
|
|
|
323
333
|
def on_mqtt_connect(self, mqtt_client, userdata, flags, rc):
|
|
@@ -425,8 +435,14 @@ class GenericGatewayHandler(
|
|
|
425
435
|
base_type='binary-sensor', alarm_category='security'
|
|
426
436
|
):
|
|
427
437
|
if sensor.id not in self.sensors_on_watch[state.id]:
|
|
428
|
-
|
|
429
|
-
|
|
438
|
+
# Register callback only when MQTT subscription succeeds
|
|
439
|
+
try:
|
|
440
|
+
sensor.on_change(self.security_sensor_change)
|
|
441
|
+
except Exception:
|
|
442
|
+
# Leave it untracked so we retry on next tick
|
|
443
|
+
raise
|
|
444
|
+
else:
|
|
445
|
+
self.sensors_on_watch[state.id][sensor.id] = i_id
|
|
430
446
|
|
|
431
447
|
if state.controller._check_is_away(self.last_sensor_actions.get(i_id, 0)):
|
|
432
448
|
if state.value != 'away':
|
|
@@ -21,7 +21,7 @@ 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=jtoG91nYlUPW-Pkl025bBoGELWFm8SQD3duTPsjRhfk,12230
|
|
23
23
|
simo/automation/forms.py,sha256=NAQUS9qrXp2d4GuvdpVyWj0Yh7vqMyX6pzs5oX4ze5Y,9800
|
|
24
|
-
simo/automation/gateways.py,sha256=
|
|
24
|
+
simo/automation/gateways.py,sha256=WhZNliJhpuf8FnHGqIaWXzW3qcac5AtqpU9THPYYgw4,17770
|
|
25
25
|
simo/automation/helpers.py,sha256=iP-fxxB8HsFQy3k2CjFubu86aMqvWgmh-p24DiyOrek,4330
|
|
26
26
|
simo/automation/models.py,sha256=zt-jkzyq5ddqGT864OkJzCsvov2vZ0nO4ez3hAeZkXg,934
|
|
27
27
|
simo/automation/serializers.py,sha256=Pg-hMaASQPB5_BTAMkfqM6z4jdHWH8xMYWOvDxIvmx8,2126
|
|
@@ -102,13 +102,13 @@ simo/core/auto_urls.py,sha256=fM9Tqzt0OfJ2FNnePGp7LcbJAWzgEwaNAJy7FNXHY-o,1299
|
|
|
102
102
|
simo/core/autocomplete_views.py,sha256=x3MKOZvXYS3xVQ-V1S7Liv_U5bxr-uc0gePa85wv5nA,4561
|
|
103
103
|
simo/core/base_types.py,sha256=FNIS9Y7wmdbVl-dISLdSBYvMEiV4zSLpBOBDYOVyam0,6580
|
|
104
104
|
simo/core/context.py,sha256=LKw1I4iIRnlnzoTCuSLLqDX7crHdBnMo3hjqYvVmzFc,1557
|
|
105
|
-
simo/core/controllers.py,sha256=
|
|
105
|
+
simo/core/controllers.py,sha256=uEC4hSeSpyHLZjojIdMPrzy2-JiK9DkMmCZpE3BcK-I,48651
|
|
106
106
|
simo/core/dynamic_settings.py,sha256=bUs58XEZOCIEhg1TigR3LmYggli13KMryBZ9pC7ugAQ,1872
|
|
107
|
-
simo/core/events.py,sha256=
|
|
107
|
+
simo/core/events.py,sha256=mjrYsByw0hGct4rXoLmnbBodViN8S1ReBIZG3g660Yo,6735
|
|
108
108
|
simo/core/filters.py,sha256=6wbn8C2WvKTTjtfMwwLBp2Fib1V0-DMpS4iqJd6jJQo,2540
|
|
109
109
|
simo/core/form_fields.py,sha256=b4wZ4n7OO0m0_BPPS9ILVrwBvhhjUB079YrroveFUWA,5222
|
|
110
|
-
simo/core/forms.py,sha256=
|
|
111
|
-
simo/core/gateways.py,sha256=
|
|
110
|
+
simo/core/forms.py,sha256=koGOIVt3gE1eejpjHFjAH-QZ1Zm2Woy51F5txh_AJVI,26191
|
|
111
|
+
simo/core/gateways.py,sha256=cM_du3VsHbSYUSWL5JHxXMV8yn6s-QrSzB3WreglGjw,4736
|
|
112
112
|
simo/core/loggers.py,sha256=EBdq23gTQScVfQVH-xeP90-wII2DQFDjoROAW6ggUP4,1645
|
|
113
113
|
simo/core/managers.py,sha256=Ampwe5K7gfE6IJULNCV35V8ysmMOdS_wz7mRzfaLZUw,3014
|
|
114
114
|
simo/core/mcp.py,sha256=MDx_m6BmkYDxCrfFegchz6NMCDCB0Mrbjx4gb2iJxHU,5188
|
|
@@ -296,7 +296,7 @@ simo/core/management/_hub_template/hub/__pycache__/settings.cpython-312.pyc,sha2
|
|
|
296
296
|
simo/core/management/_hub_template/hub/__pycache__/urls.cpython-312.pyc,sha256=dNFa6cLiHRIB5Efyaxd9SmC8hPbMkpvp8zvtkiwt1hc,178
|
|
297
297
|
simo/core/management/_hub_template/hub/__pycache__/wsgi.cpython-312.pyc,sha256=oA9duba-bsc_OOQnUaz_qiaB-0OcsS5dfjSVHTscrUM,529
|
|
298
298
|
simo/core/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
299
|
-
simo/core/management/commands/gateways_manager.py,sha256=
|
|
299
|
+
simo/core/management/commands/gateways_manager.py,sha256=_PlFQ40rtqML38RC0U_KFMbqHma-ACNgEQ_iUwvwUBs,7276
|
|
300
300
|
simo/core/management/commands/on_http_start.py,sha256=kxtWB3lOSyQA8tVmZRmLmaoVsliy5mhZp9_wwCvkj_A,6279
|
|
301
301
|
simo/core/management/commands/republish_mqtt_state.py,sha256=mc8e7qnhDyC9_fiYr6d0g2s_3wGUXCrLo95-HBIrkOA,2248
|
|
302
302
|
simo/core/management/commands/run_app_mqtt_control.py,sha256=-eDDAXbGtjqN1OK7Ym2FIdP1wVNJYR0DdvIyRzmXPDc,5592
|
|
@@ -10675,7 +10675,7 @@ simo/generic/app_widgets.py,sha256=y8W3jR76Hh26O9pPQyg2SophMbYIOtAWD33MPKbB8Mg,8
|
|
|
10675
10675
|
simo/generic/base_types.py,sha256=gJUJYpd_gE-f1ogzagAPA1u2TYljhyU0_SMlgGUvCVk,2318
|
|
10676
10676
|
simo/generic/controllers.py,sha256=oQ7mLYU-MxO4lpCQEGJA0ClIw253Ayzx3pOAFbkqC_4,53273
|
|
10677
10677
|
simo/generic/forms.py,sha256=0RIDtLLzCkiSb9OxlioicOQW9yp1OjLKekpjbxzGVfM,26272
|
|
10678
|
-
simo/generic/gateways.py,sha256=
|
|
10678
|
+
simo/generic/gateways.py,sha256=HWLwr9hmZPNbMRFiKtsjYhKxLPHlTqoIUBCmFD0JB08,22234
|
|
10679
10679
|
simo/generic/models.py,sha256=59fkYowOX0imviIhA6uwupvuharrpBykmBm674rJNoI,7279
|
|
10680
10680
|
simo/generic/routing.py,sha256=elQVZmgnPiieEuti4sJ7zITk1hlRxpgbotcutJJgC60,228
|
|
10681
10681
|
simo/generic/socket_consumers.py,sha256=qesKZVhI56Kh7vdIUDD3hzDUi0FcXwIfcmE_a3YS6JQ,1772
|
|
@@ -11034,9 +11034,9 @@ simo/users/templates/invitations/expired_msg.html,sha256=47DEQpj8HBSa-_TImW-5JCe
|
|
|
11034
11034
|
simo/users/templates/invitations/expired_suggestion.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11035
11035
|
simo/users/templates/invitations/taken_msg.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11036
11036
|
simo/users/templates/invitations/taken_suggestion.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11037
|
-
simo-3.1.
|
|
11038
|
-
simo-3.1.
|
|
11039
|
-
simo-3.1.
|
|
11040
|
-
simo-3.1.
|
|
11041
|
-
simo-3.1.
|
|
11042
|
-
simo-3.1.
|
|
11037
|
+
simo-3.1.7.dist-info/licenses/LICENSE.md,sha256=M7wm1EmMGDtwPRdg7kW4d00h1uAXjKOT3HFScYQMeiE,34916
|
|
11038
|
+
simo-3.1.7.dist-info/METADATA,sha256=478oQ_ahEy-rp4E2VvKgkzhLPFYrTOMG7IJAttlmJXM,2224
|
|
11039
|
+
simo-3.1.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
11040
|
+
simo-3.1.7.dist-info/entry_points.txt,sha256=S9PwnUYmTSW7681GKDCxUbL0leRJIaRk6fDQIKgbZBA,135
|
|
11041
|
+
simo-3.1.7.dist-info/top_level.txt,sha256=GmS1hrAbpVqn9OWZh6UX82eIOdRLgYA82RG9fe8v4Rs,5
|
|
11042
|
+
simo-3.1.7.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|