simo 1.7.19__py3-none-any.whl → 2.0.0__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/__pycache__/asgi.cpython-38.pyc +0 -0
- simo/__pycache__/on_http_start.cpython-38.pyc +0 -0
- simo/__pycache__/settings.cpython-38.pyc +0 -0
- simo/__pycache__/urls.cpython-38.pyc +0 -0
- simo/__pycache__/wsgi.cpython-38.pyc +0 -0
- simo/core/__pycache__/__init__.cpython-38.pyc +0 -0
- simo/core/__pycache__/admin.cpython-38.pyc +0 -0
- simo/core/__pycache__/api.cpython-38.pyc +0 -0
- simo/core/__pycache__/api_meta.cpython-38.pyc +0 -0
- simo/core/__pycache__/auto_urls.cpython-38.pyc +0 -0
- simo/core/__pycache__/autocomplete_views.cpython-38.pyc +0 -0
- simo/core/__pycache__/base_types.cpython-38.pyc +0 -0
- simo/core/__pycache__/context.cpython-38.pyc +0 -0
- simo/core/__pycache__/controllers.cpython-38.pyc +0 -0
- simo/core/__pycache__/events.cpython-38.pyc +0 -0
- simo/core/__pycache__/filters.cpython-38.pyc +0 -0
- simo/core/__pycache__/forms.cpython-38.pyc +0 -0
- simo/core/__pycache__/gateways.cpython-38.pyc +0 -0
- simo/core/__pycache__/managers.cpython-38.pyc +0 -0
- simo/core/__pycache__/middleware.cpython-38.pyc +0 -0
- simo/core/__pycache__/models.cpython-38.pyc +0 -0
- simo/core/__pycache__/permissions.cpython-38.pyc +0 -0
- simo/core/__pycache__/routing.cpython-38.pyc +0 -0
- simo/core/__pycache__/serializers.cpython-38.pyc +0 -0
- simo/core/__pycache__/signal_receivers.cpython-38.pyc +0 -0
- simo/core/__pycache__/socket_consumers.cpython-38.pyc +0 -0
- simo/core/__pycache__/tasks.cpython-38.pyc +0 -0
- simo/core/__pycache__/todos.cpython-38.pyc +0 -0
- simo/core/__pycache__/views.cpython-38.pyc +0 -0
- simo/core/admin.py +28 -18
- simo/core/api.py +157 -16
- simo/core/api_meta.py +87 -0
- simo/core/auto_urls.py +4 -1
- simo/core/autocomplete_views.py +8 -4
- simo/core/base_types.py +1 -0
- simo/core/context.py +3 -1
- simo/core/controllers.py +112 -32
- simo/core/db_backend/base.py +7 -22
- simo/core/drf_braces/README +3 -0
- simo/core/drf_braces/__init__.py +7 -0
- simo/core/drf_braces/__pycache__/__init__.cpython-38.pyc +0 -0
- simo/core/drf_braces/__pycache__/utils.cpython-38.pyc +0 -0
- simo/core/drf_braces/fields/__init__.py +5 -0
- simo/core/drf_braces/fields/__pycache__/__init__.cpython-38.pyc +0 -0
- simo/core/drf_braces/fields/__pycache__/_fields.cpython-38.pyc +0 -0
- simo/core/drf_braces/fields/__pycache__/custom.cpython-38.pyc +0 -0
- simo/core/drf_braces/fields/__pycache__/mixins.cpython-38.pyc +0 -0
- simo/core/drf_braces/fields/__pycache__/modified.cpython-38.pyc +0 -0
- simo/core/drf_braces/fields/_fields.py +48 -0
- simo/core/drf_braces/fields/custom.py +107 -0
- simo/core/drf_braces/fields/mixins.py +58 -0
- simo/core/drf_braces/fields/modified.py +41 -0
- simo/core/drf_braces/forms/__init__.py +0 -0
- simo/core/drf_braces/forms/fields.py +20 -0
- simo/core/drf_braces/forms/serializer_form.py +156 -0
- simo/core/drf_braces/mixins.py +52 -0
- simo/core/drf_braces/models.py +0 -0
- simo/core/drf_braces/parsers.py +72 -0
- simo/core/drf_braces/renderers.py +37 -0
- simo/core/drf_braces/serializers/__init__.py +0 -0
- simo/core/drf_braces/serializers/__pycache__/__init__.cpython-38.pyc +0 -0
- simo/core/drf_braces/serializers/__pycache__/form_serializer.cpython-38.pyc +0 -0
- simo/core/drf_braces/serializers/enforce_validation_serializer.py +214 -0
- simo/core/drf_braces/serializers/form_serializer.py +391 -0
- simo/core/drf_braces/serializers/swapping.py +48 -0
- simo/core/drf_braces/tests/__init__.py +0 -0
- simo/core/drf_braces/tests/fields/__init__.py +0 -0
- simo/core/drf_braces/tests/fields/test_custom.py +94 -0
- simo/core/drf_braces/tests/fields/test_fields.py +13 -0
- simo/core/drf_braces/tests/fields/test_mixins.py +96 -0
- simo/core/drf_braces/tests/fields/test_modified.py +40 -0
- simo/core/drf_braces/tests/forms/__init__.py +0 -0
- simo/core/drf_braces/tests/forms/test_fields.py +46 -0
- simo/core/drf_braces/tests/forms/test_serializer_form.py +256 -0
- simo/core/drf_braces/tests/serializers/__init__.py +0 -0
- simo/core/drf_braces/tests/serializers/test_enforce_validation_serializer.py +169 -0
- simo/core/drf_braces/tests/serializers/test_form_serializer.py +387 -0
- simo/core/drf_braces/tests/serializers/test_swapping.py +40 -0
- simo/core/drf_braces/tests/test_mixins.py +111 -0
- simo/core/drf_braces/tests/test_parsers.py +73 -0
- simo/core/drf_braces/tests/test_renderers.py +23 -0
- simo/core/drf_braces/tests/test_utils.py +73 -0
- simo/core/drf_braces/utils.py +209 -0
- simo/core/events.py +3 -3
- simo/core/forms.py +79 -37
- simo/core/gateways.py +31 -14
- simo/core/management/__pycache__/__init__.cpython-38.pyc +0 -0
- simo/core/management/commands/__pycache__/__init__.cpython-38.pyc +0 -0
- simo/core/management/commands/gateways_manager.py +0 -1
- simo/core/managers.py +81 -0
- simo/core/middleware.py +25 -0
- simo/core/migrations/0026_category_instance.py +20 -0
- simo/core/migrations/0027_remove_component_tags.py +17 -0
- simo/core/migrations/0028_rename_subcomponents_component_slaves.py +18 -0
- simo/core/migrations/0029_auto_20240229_1331.py +33 -0
- simo/core/migrations/__pycache__/0026_category_instance.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0027_remove_component_tags.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0028_rename_subcomponents_component_slaves.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0029_auto_20240229_1331.cpython-38.pyc +0 -0
- simo/core/models.py +103 -66
- simo/core/permissions.py +28 -2
- simo/core/serializers.py +330 -26
- simo/core/socket_consumers.py +5 -14
- simo/core/tasks.py +11 -1
- simo/core/templates/admin/base.html +37 -10
- simo/core/templates/admin/wizard/discovery.html +188 -0
- simo/core/templates/admin/wizard/wizard_add.html +5 -5
- simo/core/utils/__pycache__/serialization.cpython-38.pyc +0 -0
- simo/core/utils/admin.py +9 -2
- simo/core/utils/formsets.py +17 -16
- simo/core/utils/helpers.py +1 -0
- simo/core/utils/serialization.py +56 -0
- simo/core/utils/type_constants.py +1 -1
- simo/core/utils/validators.py +14 -1
- simo/core/views.py +13 -0
- simo/fleet/__pycache__/admin.cpython-38.pyc +0 -0
- simo/fleet/__pycache__/api.cpython-38.pyc +0 -0
- simo/fleet/__pycache__/auto_urls.cpython-38.pyc +0 -0
- simo/fleet/__pycache__/controllers.cpython-38.pyc +0 -0
- simo/fleet/__pycache__/forms.cpython-38.pyc +0 -0
- simo/fleet/__pycache__/gateways.cpython-38.pyc +0 -0
- simo/fleet/__pycache__/managers.cpython-38.pyc +0 -0
- simo/fleet/__pycache__/models.cpython-38.pyc +0 -0
- simo/fleet/__pycache__/serializers.cpython-38.pyc +0 -0
- simo/fleet/__pycache__/socket_consumers.cpython-38.pyc +0 -0
- simo/fleet/__pycache__/utils.cpython-38.pyc +0 -0
- simo/fleet/__pycache__/views.cpython-38.pyc +0 -0
- simo/fleet/admin.py +54 -25
- simo/fleet/api.py +59 -3
- simo/fleet/auto_urls.py +2 -3
- simo/fleet/controllers.py +199 -16
- simo/fleet/forms.py +325 -483
- simo/fleet/gateways.py +44 -2
- simo/fleet/managers.py +32 -0
- simo/fleet/migrations/0025_auto_20240130_1334.py +27 -0
- simo/fleet/migrations/0026_rename_i2cinterface_scl_pin_and_more.py +64 -0
- simo/fleet/migrations/0027_auto_20240306_0802.py +170 -0
- simo/fleet/migrations/0028_remove_i2cinterface_scl_pin_no_and_more.py +21 -0
- simo/fleet/migrations/0029_alter_i2cinterface_scl_pin_and_more.py +24 -0
- simo/fleet/migrations/0030_colonelpin_label_alter_colonel_type.py +24 -0
- simo/fleet/migrations/0031_alter_colonel_type.py +18 -0
- simo/fleet/migrations/__pycache__/0025_auto_20240130_1334.cpython-38.pyc +0 -0
- simo/fleet/migrations/__pycache__/0026_rename_i2cinterface_scl_pin_and_more.cpython-38.pyc +0 -0
- simo/fleet/migrations/__pycache__/0027_auto_20240306_0802.cpython-38.pyc +0 -0
- simo/fleet/migrations/__pycache__/0028_remove_i2cinterface_scl_pin_no_and_more.cpython-38.pyc +0 -0
- simo/fleet/migrations/__pycache__/0029_alter_i2cinterface_scl_pin_and_more.cpython-38.pyc +0 -0
- simo/fleet/migrations/__pycache__/0030_colonelpin_label_alter_colonel_type.cpython-38.pyc +0 -0
- simo/fleet/migrations/__pycache__/0031_alter_colonel_type.cpython-38.pyc +0 -0
- simo/fleet/models.py +134 -82
- simo/fleet/serializers.py +35 -1
- simo/fleet/socket_consumers.py +239 -76
- simo/fleet/utils.py +15 -53
- simo/fleet/views.py +28 -14
- simo/generic/controllers.py +13 -89
- simo/generic/forms.py +30 -23
- simo/generic/gateways.py +98 -10
- simo/generic/models.py +3 -3
- simo/multimedia/controllers.py +9 -8
- simo/settings.py +7 -4
- simo/urls.py +4 -8
- simo/users/__pycache__/admin.cpython-38.pyc +0 -0
- simo/users/__pycache__/api.cpython-38.pyc +0 -0
- simo/users/__pycache__/auto_urls.cpython-38.pyc +0 -0
- simo/users/__pycache__/models.cpython-38.pyc +0 -0
- simo/users/__pycache__/serializers.cpython-38.pyc +0 -0
- simo/users/__pycache__/sso_urls.cpython-38.pyc +0 -0
- simo/users/admin.py +8 -1
- simo/users/api.py +38 -2
- simo/users/auto_urls.py +2 -2
- simo/users/migrations/0025_rename_name_fingerprint_type_and_more.py +22 -0
- simo/users/migrations/__pycache__/0025_rename_name_fingerprint_type_and_more.cpython-38.pyc +0 -0
- simo/users/models.py +2 -3
- simo/users/serializers.py +15 -1
- simo/users/sso_urls.py +3 -3
- simo/wsgi.py +7 -0
- {simo-1.7.19.dist-info → simo-2.0.0.dist-info}/METADATA +8 -9
- {simo-1.7.19.dist-info → simo-2.0.0.dist-info}/RECORD +180 -210
- {simo-1.7.19.dist-info → simo-2.0.0.dist-info}/WHEEL +1 -1
- simo/core/__pycache__/__init__.cpython-37.pyc +0 -0
- simo/core/__pycache__/admin.cpython-37.pyc +0 -0
- simo/core/__pycache__/api.cpython-37.pyc +0 -0
- simo/core/__pycache__/apps.cpython-38.pyc +0 -0
- simo/core/__pycache__/controllers.cpython-37.pyc +0 -0
- simo/core/__pycache__/events.cpython-37.pyc +0 -0
- simo/core/__pycache__/forms.cpython-37.pyc +0 -0
- simo/core/__pycache__/gateways.cpython-37.pyc +0 -0
- simo/core/__pycache__/models.cpython-37.pyc +0 -0
- simo/core/__pycache__/scripts.cpython-37.pyc +0 -0
- simo/core/__pycache__/serializers.cpython-37.pyc +0 -0
- simo/core/__pycache__/widgets.cpython-37.pyc +0 -0
- simo/core/db_backend/__pycache__/__init__.cpython-38.pyc +0 -0
- simo/core/db_backend/__pycache__/base.cpython-38.pyc +0 -0
- simo/core/management/commands/__pycache__/gateways_manager.cpython-38.pyc +0 -0
- simo/core/management/commands/__pycache__/run_gateway.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0001_initial.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0002_load_icons.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0003_create_default_zones_and_categories.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0004_create_generic.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0005_component_subcomponents.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0006_alter_component_subcomponents.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0007_component_change_init_to.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0008_alter_component_change_init_to.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0009_auto_20220707_1404.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0010_historyaggregate.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0011_component_last_change.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0012_instance.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0013_auto_20231003_0754.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0014_zone_instance.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0015_auto_20231004_1113.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0016_auto_20231004_1113.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0017_auto_20231004_1313.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0018_auto_20231005_0622.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0019_alter_gateway_type.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0020_component_meta.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0021_auto_20231020_1041.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/__init__.cpython-38.pyc +0 -0
- simo/core/templatetags/__pycache__/__init__.cpython-38.pyc +0 -0
- simo/core/templatetags/__pycache__/components_list.cpython-38.pyc +0 -0
- simo/core/utils/__pycache__/__init__.cpython-38.pyc +0 -0
- simo/core/utils/__pycache__/admin.cpython-38.pyc +0 -0
- simo/core/utils/__pycache__/config_values.cpython-38.pyc +0 -0
- simo/core/utils/__pycache__/easing.cpython-38.pyc +0 -0
- simo/core/utils/__pycache__/form_fields.cpython-38.pyc +0 -0
- simo/core/utils/__pycache__/form_widgets.cpython-38.pyc +0 -0
- simo/core/utils/__pycache__/formsets.cpython-38.pyc +0 -0
- simo/core/utils/__pycache__/helpers.cpython-38.pyc +0 -0
- simo/core/utils/__pycache__/logs.cpython-38.pyc +0 -0
- simo/core/utils/__pycache__/mixins.cpython-38.pyc +0 -0
- simo/core/utils/__pycache__/model_helpers.cpython-38.pyc +0 -0
- simo/core/utils/__pycache__/relay.cpython-38.pyc +0 -0
- simo/core/utils/__pycache__/type_constants.cpython-38.pyc +0 -0
- simo/core/utils/__pycache__/validators.cpython-38.pyc +0 -0
- simo/fleet/tasks.py +0 -25
- simo/generic/__pycache__/__init__.cpython-37.pyc +0 -0
- simo/generic/__pycache__/__init__.cpython-38.pyc +0 -0
- simo/generic/__pycache__/app_widgets.cpython-38.pyc +0 -0
- simo/generic/__pycache__/base_types.cpython-38.pyc +0 -0
- simo/generic/__pycache__/controllers.cpython-37.pyc +0 -0
- simo/generic/__pycache__/controllers.cpython-38.pyc +0 -0
- simo/generic/__pycache__/forms.cpython-38.pyc +0 -0
- simo/generic/__pycache__/gateways.cpython-38.pyc +0 -0
- simo/generic/__pycache__/models.cpython-38.pyc +0 -0
- simo/generic/__pycache__/routing.cpython-38.pyc +0 -0
- simo/generic/__pycache__/socket_consumers.cpython-38.pyc +0 -0
- simo/generic/__pycache__/tasks.cpython-38.pyc +0 -0
- simo/generic/__pycache__/widgets.cpython-37.pyc +0 -0
- simo/generic/tasks.py +0 -41
- simo/multimedia/__pycache__/__init__.cpython-38.pyc +0 -0
- simo/multimedia/__pycache__/admin.cpython-38.pyc +0 -0
- simo/multimedia/__pycache__/api.cpython-38.pyc +0 -0
- simo/multimedia/__pycache__/app_widgets.cpython-38.pyc +0 -0
- simo/multimedia/__pycache__/base_types.cpython-38.pyc +0 -0
- simo/multimedia/__pycache__/controllers.cpython-38.pyc +0 -0
- simo/multimedia/__pycache__/forms.cpython-38.pyc +0 -0
- simo/multimedia/__pycache__/models.cpython-38.pyc +0 -0
- simo/multimedia/__pycache__/serializers.cpython-38.pyc +0 -0
- simo/multimedia/migrations/__pycache__/0001_initial.cpython-38.pyc +0 -0
- simo/multimedia/migrations/__pycache__/0002_sound_length.cpython-38.pyc +0 -0
- simo/multimedia/migrations/__pycache__/0003_alter_sound_length.cpython-38.pyc +0 -0
- simo/multimedia/migrations/__pycache__/0004_auto_20231023_1055.cpython-38.pyc +0 -0
- simo/multimedia/migrations/__pycache__/__init__.cpython-38.pyc +0 -0
- simo/notifications/__pycache__/__init__.cpython-38.pyc +0 -0
- simo/notifications/__pycache__/admin.cpython-38.pyc +0 -0
- simo/notifications/__pycache__/api.cpython-38.pyc +0 -0
- simo/notifications/__pycache__/models.cpython-38.pyc +0 -0
- simo/notifications/__pycache__/serializers.cpython-38.pyc +0 -0
- simo/notifications/__pycache__/utils.cpython-38.pyc +0 -0
- simo/notifications/migrations/__pycache__/0001_initial.cpython-38.pyc +0 -0
- simo/notifications/migrations/__pycache__/0002_notification_instance.cpython-38.pyc +0 -0
- simo/notifications/migrations/__pycache__/__init__.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0001_initial.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0002_componentpermission.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0003_create_roles_and_system_user.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0004_user_secret_key.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0005_permissionsrole_instance.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0006_auto_20231003_0850.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0007_auto_20231003_1228.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0008_auto_20231003_1229.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0009_remove_user_role.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0010_auto_20231004_1313.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0011_auto_20231004_1313.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0012_alter_userinstancerole_unique_together.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0013_remove_user_roles.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0014_user_roles.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0015_remove_user_at_home.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0016_auto_20231005_1050.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/__init__.cpython-38.pyc +0 -0
- {simo-1.7.19.dist-info → simo-2.0.0.dist-info}/LICENSE.md +0 -0
- {simo-1.7.19.dist-info → simo-2.0.0.dist-info}/top_level.txt +0 -0
simo/generic/controllers.py
CHANGED
|
@@ -63,7 +63,7 @@ class Script(ControllerBase, TimerMixin):
|
|
|
63
63
|
)
|
|
64
64
|
return value
|
|
65
65
|
|
|
66
|
-
def
|
|
66
|
+
def _prepare_for_send(self, value):
|
|
67
67
|
if value == 'start':
|
|
68
68
|
new_code = getattr(self.component, 'new_code', None)
|
|
69
69
|
if new_code:
|
|
@@ -71,7 +71,7 @@ class Script(ControllerBase, TimerMixin):
|
|
|
71
71
|
self.component.refresh_from_db()
|
|
72
72
|
self.component.config['code'] = new_code
|
|
73
73
|
self.component.save(update_fields=['config'])
|
|
74
|
-
return
|
|
74
|
+
return value
|
|
75
75
|
|
|
76
76
|
def _val_to_success(self, value):
|
|
77
77
|
if value == 'start':
|
|
@@ -208,9 +208,13 @@ class Thermostat(ControllerBase):
|
|
|
208
208
|
heater = Component.objects.filter(
|
|
209
209
|
pk=self.component.config.get('heater')
|
|
210
210
|
).first()
|
|
211
|
+
if heater:
|
|
212
|
+
heater.prepare_controller()
|
|
211
213
|
cooler = Component.objects.filter(
|
|
212
214
|
pk=self.component.config.get('cooler')
|
|
213
215
|
).first()
|
|
216
|
+
if cooler:
|
|
217
|
+
cooler.prepare_controller()
|
|
214
218
|
|
|
215
219
|
if not temperature_sensor or not temperature_sensor.alive:
|
|
216
220
|
print(f"No temperature sensor on {self.component}!")
|
|
@@ -322,47 +326,6 @@ class AlarmGroup(ControllerBase):
|
|
|
322
326
|
)
|
|
323
327
|
return value
|
|
324
328
|
|
|
325
|
-
def _send_to_device(self, value):
|
|
326
|
-
assert value in ('armed', 'disarmed')
|
|
327
|
-
|
|
328
|
-
other_alarm_groups = {}
|
|
329
|
-
stats = {
|
|
330
|
-
'disarmed': 0, 'pending-arm': 0, 'armed': 0, 'breached': 0
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
for c_id in self.component.config['components']:
|
|
334
|
-
slave = Component.objects.filter(pk=c_id).first()
|
|
335
|
-
if not slave:
|
|
336
|
-
continue
|
|
337
|
-
if value == 'armed':
|
|
338
|
-
if not slave.is_in_alarm():
|
|
339
|
-
slave.arm_status = 'armed'
|
|
340
|
-
stats['armed'] += 1
|
|
341
|
-
else:
|
|
342
|
-
slave.arm_status = 'pending-arm'
|
|
343
|
-
stats['pending-arm'] += 1
|
|
344
|
-
elif value == 'disarmed':
|
|
345
|
-
stats['disarmed'] += 1
|
|
346
|
-
slave.arm_status = 'disarmed'
|
|
347
|
-
|
|
348
|
-
slave.do_not_update_alarm_group = True
|
|
349
|
-
slave.save(update_fields=['arm_status'])
|
|
350
|
-
|
|
351
|
-
for other_group in Component.objects.filter(
|
|
352
|
-
controller_uid=AlarmGroup.uid,
|
|
353
|
-
config__components__contains=slave.id
|
|
354
|
-
).exclude(pk=self.component.pk):
|
|
355
|
-
other_alarm_groups[other_group.pk] = other_group
|
|
356
|
-
|
|
357
|
-
self.component.value = value
|
|
358
|
-
if stats['pending-arm']:
|
|
359
|
-
self.component.value = 'pending-arm'
|
|
360
|
-
self.component.config['stats'] = stats
|
|
361
|
-
self.component.save()
|
|
362
|
-
|
|
363
|
-
for pk, other_group in other_alarm_groups.items():
|
|
364
|
-
other_group.refresh_status()
|
|
365
|
-
|
|
366
329
|
def arm(self):
|
|
367
330
|
self.send('armed')
|
|
368
331
|
|
|
@@ -375,7 +338,6 @@ class AlarmGroup(ControllerBase):
|
|
|
375
338
|
)
|
|
376
339
|
|
|
377
340
|
def refresh_status(self):
|
|
378
|
-
|
|
379
341
|
stats = {
|
|
380
342
|
'disarmed': 0, 'pending-arm': 0, 'armed': 0, 'breached': 0
|
|
381
343
|
}
|
|
@@ -515,21 +477,6 @@ class Gate(ControllerBase, TimerMixin):
|
|
|
515
477
|
self.component.set(self.component.value + '_moving')
|
|
516
478
|
threading.Thread(target=cancel_move, daemon=True).start()
|
|
517
479
|
|
|
518
|
-
def _send_to_device(self, value):
|
|
519
|
-
switch = Component.objects.filter(
|
|
520
|
-
pk=self.component.config.get('action_switch')
|
|
521
|
-
).first()
|
|
522
|
-
if not switch:
|
|
523
|
-
return
|
|
524
|
-
|
|
525
|
-
if self.component.config.get('action_method') == 'click':
|
|
526
|
-
switch.click()
|
|
527
|
-
else:
|
|
528
|
-
if value == 'open':
|
|
529
|
-
switch.turn_on()
|
|
530
|
-
else:
|
|
531
|
-
switch.turn_off()
|
|
532
|
-
|
|
533
480
|
def open(self):
|
|
534
481
|
self.send('open')
|
|
535
482
|
|
|
@@ -662,9 +609,6 @@ class Blinds(ControllerBase, TimerMixin):
|
|
|
662
609
|
|
|
663
610
|
return value
|
|
664
611
|
|
|
665
|
-
def _send_to_device(self, value):
|
|
666
|
-
GatewayObjectCommand(self.component, **{'set_val': value}).publish()
|
|
667
|
-
|
|
668
612
|
def open(self):
|
|
669
613
|
self.send(0)
|
|
670
614
|
|
|
@@ -776,6 +720,7 @@ class Watering(ControllerBase):
|
|
|
776
720
|
switch = Component.objects.get(pk=contour_data['switch'])
|
|
777
721
|
except Component.DoesNotExist:
|
|
778
722
|
continue
|
|
723
|
+
switch.prepare_controller()
|
|
779
724
|
if run:
|
|
780
725
|
if switch.timer_engaged():
|
|
781
726
|
switch.stop_timer()
|
|
@@ -900,6 +845,7 @@ class Watering(ControllerBase):
|
|
|
900
845
|
switch = Component.objects.get(pk=contour_data['switch'])
|
|
901
846
|
except Component.DoesNotExist:
|
|
902
847
|
continue
|
|
848
|
+
switch.prepare_controller()
|
|
903
849
|
if switch.timer_engaged():
|
|
904
850
|
switch.stop_timer()
|
|
905
851
|
switch.turn_off()
|
|
@@ -1131,6 +1077,7 @@ class AlarmClock(ControllerBase):
|
|
|
1131
1077
|
print(f"Reverse event {event['uid']}!")
|
|
1132
1078
|
comp = Component.objects.filter(id=event['component']).first()
|
|
1133
1079
|
if comp:
|
|
1080
|
+
comp.prepare_controller()
|
|
1134
1081
|
if forward:
|
|
1135
1082
|
action_name = 'play_action'
|
|
1136
1083
|
else:
|
|
@@ -1370,7 +1317,8 @@ class AlarmClock(ControllerBase):
|
|
|
1370
1317
|
return current_value
|
|
1371
1318
|
|
|
1372
1319
|
|
|
1373
|
-
|
|
1320
|
+
# ----------- Dummy controllers -----------------------------
|
|
1321
|
+
|
|
1374
1322
|
class StateSelect(ControllerBase):
|
|
1375
1323
|
gateway_class = DummyGatewayHandler
|
|
1376
1324
|
name = _("State select")
|
|
@@ -1387,9 +1335,6 @@ class StateSelect(ControllerBase):
|
|
|
1387
1335
|
raise ValidationError("Unsupported value!")
|
|
1388
1336
|
return value
|
|
1389
1337
|
|
|
1390
|
-
def _send_to_device(self, value):
|
|
1391
|
-
self.component.set(value)
|
|
1392
|
-
|
|
1393
1338
|
|
|
1394
1339
|
class DummyBinarySensor(BinarySensor):
|
|
1395
1340
|
gateway_class = DummyGatewayHandler
|
|
@@ -1406,56 +1351,35 @@ class DummyMultiSensor(MultiSensor):
|
|
|
1406
1351
|
class DummySwitch(Switch):
|
|
1407
1352
|
gateway_class = DummyGatewayHandler
|
|
1408
1353
|
|
|
1409
|
-
def _send_to_device(self, value):
|
|
1410
|
-
self.component.set(value)
|
|
1411
|
-
|
|
1412
1354
|
|
|
1413
1355
|
class DummyDoubleSwitch(DoubleSwitch):
|
|
1414
1356
|
gateway_class = DummyGatewayHandler
|
|
1415
1357
|
|
|
1416
|
-
def _send_to_device(self, value):
|
|
1417
|
-
self.component.set(value)
|
|
1418
|
-
|
|
1419
1358
|
|
|
1420
1359
|
class DummyTripleSwitch(TripleSwitch):
|
|
1421
1360
|
gateway_class = DummyGatewayHandler
|
|
1422
1361
|
|
|
1423
|
-
def _send_to_device(self, value):
|
|
1424
|
-
self.component.set(value)
|
|
1425
|
-
|
|
1426
1362
|
|
|
1427
1363
|
class DummyQuadrupleSwitch(QuadrupleSwitch):
|
|
1428
1364
|
gateway_class = DummyGatewayHandler
|
|
1429
1365
|
|
|
1430
|
-
def _send_to_device(self, value):
|
|
1431
|
-
self.component.set(value)
|
|
1432
|
-
|
|
1433
1366
|
|
|
1434
1367
|
class DummyQuintupleSwitch(QuintupleSwitch):
|
|
1435
1368
|
gateway_class = DummyGatewayHandler
|
|
1436
1369
|
|
|
1437
|
-
def _send_to_device(self, value):
|
|
1438
|
-
self.component.set(value)
|
|
1439
|
-
|
|
1440
1370
|
|
|
1441
1371
|
class DummyDimmer(Dimmer):
|
|
1442
1372
|
gateway_class = DummyGatewayHandler
|
|
1443
1373
|
|
|
1444
|
-
def
|
|
1374
|
+
def _prepare_for_send(self, value):
|
|
1445
1375
|
if self.component.config.get('inverse'):
|
|
1446
1376
|
value = self.component.config.get('max') - value
|
|
1447
|
-
|
|
1377
|
+
return value
|
|
1448
1378
|
|
|
1449
1379
|
|
|
1450
1380
|
class DummyDimmerPlus(DimmerPlus):
|
|
1451
1381
|
gateway_class = DummyGatewayHandler
|
|
1452
1382
|
|
|
1453
|
-
def _send_to_device(self, value):
|
|
1454
|
-
self.component.set(value)
|
|
1455
|
-
|
|
1456
1383
|
|
|
1457
1384
|
class DummyRGBWLight(RGBWLight):
|
|
1458
1385
|
gateway_class = DummyGatewayHandler
|
|
1459
|
-
|
|
1460
|
-
def _send_to_device(self, value):
|
|
1461
|
-
self.component.set(value)
|
simo/generic/forms.py
CHANGED
|
@@ -14,18 +14,14 @@ from dal import autocomplete, forward
|
|
|
14
14
|
from simo.core.utils.config_values import config_to_dict
|
|
15
15
|
from simo.core.utils.formsets import FormsetField
|
|
16
16
|
from simo.core.utils.helpers import get_random_string
|
|
17
|
-
from simo.core.utils.form_fields import
|
|
17
|
+
from simo.core.utils.form_fields import ListSelect2Widget
|
|
18
18
|
from simo.conf import dynamic_settings
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
class ScriptConfigForm(BaseComponentForm):
|
|
22
22
|
autostart = forms.BooleanField(
|
|
23
23
|
initial=True, required=False,
|
|
24
|
-
help_text="
|
|
25
|
-
)
|
|
26
|
-
autorestart = forms.BooleanField(
|
|
27
|
-
initial=True, required=False,
|
|
28
|
-
help_text="Wake my script up automatically if it encounters error."
|
|
24
|
+
help_text="Wake up every 60 s if not running."
|
|
29
25
|
)
|
|
30
26
|
code = forms.CharField(widget=PythonCode)
|
|
31
27
|
log = forms.CharField(
|
|
@@ -49,7 +45,7 @@ class ScriptConfigForm(BaseComponentForm):
|
|
|
49
45
|
def get_admin_fieldsets(cls, request, obj=None):
|
|
50
46
|
base_fields = (
|
|
51
47
|
'id', 'gateway', 'base_type', 'name', 'icon', 'zone', 'category',
|
|
52
|
-
'
|
|
48
|
+
'show_in_app', 'autostart',
|
|
53
49
|
'code', 'control', 'log'
|
|
54
50
|
)
|
|
55
51
|
|
|
@@ -200,6 +196,7 @@ class AlarmGroupConfigForm(BaseComponentForm):
|
|
|
200
196
|
"Can not cover self. Please remove - [%s]" % str(check_cmp)
|
|
201
197
|
)
|
|
202
198
|
if comp.base_type == 'alarm-group':
|
|
199
|
+
comp.prepare_controller()
|
|
203
200
|
self.recurse_check_alarm_groups(
|
|
204
201
|
comp.get_children(), check_cmp
|
|
205
202
|
)
|
|
@@ -224,6 +221,7 @@ class AlarmGroupConfigForm(BaseComponentForm):
|
|
|
224
221
|
c.save(update_fields=('config',))
|
|
225
222
|
if obj.id:
|
|
226
223
|
comp = Component.objects.get(id=obj.id)
|
|
224
|
+
comp.prepare_controller()
|
|
227
225
|
comp.refresh_status()
|
|
228
226
|
return obj
|
|
229
227
|
|
|
@@ -444,21 +442,29 @@ class WateringConfigForm(BaseComponentForm):
|
|
|
444
442
|
)
|
|
445
443
|
obj = super().save(commit=commit)
|
|
446
444
|
if commit:
|
|
447
|
-
obj.
|
|
445
|
+
obj.slaves.clear()
|
|
448
446
|
for contour in self.cleaned_data['contours']:
|
|
449
|
-
obj.
|
|
447
|
+
obj.slaves.add(
|
|
450
448
|
Component.objects.get(pk=contour['switch'])
|
|
451
449
|
)
|
|
452
450
|
return obj
|
|
453
451
|
|
|
454
452
|
|
|
455
453
|
class StateForm(forms.Form):
|
|
456
|
-
icon =
|
|
454
|
+
icon = forms.CharField(
|
|
455
|
+
widget=ListSelect2Widget(
|
|
456
|
+
url='autocomplete-icon', attrs={'data-html': True}
|
|
457
|
+
)
|
|
458
|
+
)
|
|
457
459
|
slug = forms.SlugField(required=True)
|
|
458
460
|
name = forms.CharField(required=True)
|
|
459
461
|
help_text = forms.CharField(required=False, widget=forms.Textarea(attrs={'rows': 3}))
|
|
460
462
|
prefix = 'states'
|
|
461
463
|
|
|
464
|
+
def clean(self):
|
|
465
|
+
print("Let's clean the data! ", self.cleaned_data)
|
|
466
|
+
return self.cleaned_data
|
|
467
|
+
|
|
462
468
|
|
|
463
469
|
class StateSelectForm(BaseComponentForm):
|
|
464
470
|
states = FormsetField(
|
|
@@ -497,20 +503,21 @@ class AlarmClockEventForm(forms.Form):
|
|
|
497
503
|
def clean(self):
|
|
498
504
|
if not self.cleaned_data.get('component'):
|
|
499
505
|
return self.cleaned_data
|
|
500
|
-
if not self.cleaned_data.get('
|
|
506
|
+
if not self.cleaned_data.get('play_action'):
|
|
501
507
|
return self.cleaned_data
|
|
502
508
|
component = self.cleaned_data.get('component')
|
|
503
|
-
|
|
509
|
+
component.prepare_controller()
|
|
510
|
+
if not hasattr(component, self.cleaned_data['play_action']):
|
|
504
511
|
self.add_error(
|
|
505
|
-
'
|
|
506
|
-
f"{component} has no {self.cleaned_data['
|
|
512
|
+
'play_action',
|
|
513
|
+
f"{component} has no {self.cleaned_data['play_action']} action!"
|
|
507
514
|
)
|
|
508
|
-
if self.cleaned_data.get('
|
|
509
|
-
if not hasattr(component, self.cleaned_data['
|
|
515
|
+
if self.cleaned_data.get('reverse_action'):
|
|
516
|
+
if not hasattr(component, self.cleaned_data['reverse_action']):
|
|
510
517
|
self.add_error(
|
|
511
|
-
'
|
|
518
|
+
'reverse_action',
|
|
512
519
|
f"{component} has no "
|
|
513
|
-
f"{self.cleaned_data['
|
|
520
|
+
f"{self.cleaned_data['reverse_action']} action!"
|
|
514
521
|
)
|
|
515
522
|
return self.cleaned_data
|
|
516
523
|
|
|
@@ -525,16 +532,16 @@ class AlarmClockConfigForm(BaseComponentForm):
|
|
|
525
532
|
def clean_default_events(self):
|
|
526
533
|
events = self.cleaned_data['default_events']
|
|
527
534
|
for i, cont in enumerate(events):
|
|
528
|
-
if not cont
|
|
535
|
+
if not cont.get('uid'):
|
|
529
536
|
cont['uid'] = get_random_string(6)
|
|
530
537
|
return events
|
|
531
538
|
|
|
532
539
|
def save(self, commit=True):
|
|
533
540
|
obj = super().save(commit=commit)
|
|
534
541
|
if commit:
|
|
535
|
-
obj.
|
|
542
|
+
obj.slaves.clear()
|
|
536
543
|
for comp in self.cleaned_data['default_events']:
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
544
|
+
c = Component.objects.filter(pk=comp['component']).first()
|
|
545
|
+
if c:
|
|
546
|
+
obj.slaves.add(c)
|
|
540
547
|
return obj
|
simo/generic/gateways.py
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
import sys
|
|
2
|
-
import cv2
|
|
3
2
|
import logging
|
|
4
|
-
import base64
|
|
5
3
|
import pytz
|
|
6
|
-
from logging.handlers import RotatingFileHandler
|
|
7
4
|
import json
|
|
8
5
|
import time
|
|
9
6
|
import multiprocessing
|
|
10
7
|
import threading
|
|
11
8
|
from django.conf import settings
|
|
12
9
|
from django.utils import timezone
|
|
13
|
-
from django.db import
|
|
10
|
+
from django.db import connection as db_connection
|
|
11
|
+
from django.db.models import Q
|
|
14
12
|
import paho.mqtt.client as mqtt
|
|
15
13
|
from simo.core.models import Component
|
|
16
14
|
from simo.core.gateways import BaseObjectCommandsGatewayHandler
|
|
@@ -21,9 +19,6 @@ from simo.core.loggers import get_gw_logger, get_component_logger
|
|
|
21
19
|
|
|
22
20
|
|
|
23
21
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
22
|
class BlindsRunner(threading.Thread):
|
|
28
23
|
|
|
29
24
|
def __init__(self, blinds, *args, **kwargs):
|
|
@@ -168,7 +163,9 @@ class GenericGatewayHandler(BaseObjectCommandsGatewayHandler):
|
|
|
168
163
|
blinds_runners = {}
|
|
169
164
|
periodic_tasks = (
|
|
170
165
|
('watch_thermostats', 60),
|
|
171
|
-
('watch_alarm_clocks', 30)
|
|
166
|
+
('watch_alarm_clocks', 30),
|
|
167
|
+
('watch_scripts', 60),
|
|
168
|
+
('watch_watering', 60)
|
|
172
169
|
)
|
|
173
170
|
|
|
174
171
|
def watch_thermostats(self):
|
|
@@ -178,6 +175,7 @@ class GenericGatewayHandler(BaseObjectCommandsGatewayHandler):
|
|
|
178
175
|
):
|
|
179
176
|
tz = pytz.timezone(thermostat.zone.instance.timezone)
|
|
180
177
|
timezone.activate(tz)
|
|
178
|
+
thermostat.prepare_controller()
|
|
181
179
|
thermostat.evaluate()
|
|
182
180
|
|
|
183
181
|
def watch_alarm_clocks(self):
|
|
@@ -187,14 +185,37 @@ class GenericGatewayHandler(BaseObjectCommandsGatewayHandler):
|
|
|
187
185
|
):
|
|
188
186
|
tz = pytz.timezone(alarm_clock.zone.instance.timezone)
|
|
189
187
|
timezone.activate(tz)
|
|
188
|
+
alarm_clock.prepare_controller()
|
|
190
189
|
alarm_clock.tick()
|
|
191
190
|
|
|
191
|
+
def watch_scripts(self):
|
|
192
|
+
from simo.generic.controllers import Script
|
|
193
|
+
for script in Component.objects.filter(
|
|
194
|
+
controller_uid=Script.uid,
|
|
195
|
+
config__autostart=True
|
|
196
|
+
).exclude(value='running'):
|
|
197
|
+
script.prepare_controller()
|
|
198
|
+
self.start_script(script)
|
|
199
|
+
|
|
200
|
+
def watch_watering(self):
|
|
201
|
+
from .controllers import Watering
|
|
202
|
+
for watering in Component.objects.filter(controller_uid=Watering.uid):
|
|
203
|
+
tz = pytz.timezone(watering.zone.instance.timezone)
|
|
204
|
+
timezone.activate(tz)
|
|
205
|
+
watering.prepare_controller()
|
|
206
|
+
if watering.value['status'] == 'running_program':
|
|
207
|
+
watering.set_program_progress(
|
|
208
|
+
watering.value['program_progress'] + 1
|
|
209
|
+
)
|
|
210
|
+
else:
|
|
211
|
+
watering.controller._perform_schedule()
|
|
212
|
+
|
|
192
213
|
def run(self, exit):
|
|
193
214
|
self.exit = exit
|
|
194
215
|
self.logger = get_gw_logger(self.gateway_instance.id)
|
|
195
216
|
for task, period in self.periodic_tasks:
|
|
196
217
|
threading.Thread(
|
|
197
|
-
target=self._run_periodic_task, args=(task, period), daemon=True
|
|
218
|
+
target=self._run_periodic_task, args=(exit, task, period), daemon=True
|
|
198
219
|
).start()
|
|
199
220
|
|
|
200
221
|
from simo.generic.controllers import Script, IPCamera
|
|
@@ -244,7 +265,7 @@ class GenericGatewayHandler(BaseObjectCommandsGatewayHandler):
|
|
|
244
265
|
mqtt_client.subscribe(command.get_topic())
|
|
245
266
|
|
|
246
267
|
def on_mqtt_message(self, client, userdata, msg):
|
|
247
|
-
from simo.generic.controllers import Script, Blinds
|
|
268
|
+
from simo.generic.controllers import Script, Blinds, AlarmGroup, Gate
|
|
248
269
|
payload = json.loads(msg.payload)
|
|
249
270
|
component = get_event_obj(payload, Component)
|
|
250
271
|
if not component:
|
|
@@ -258,6 +279,10 @@ class GenericGatewayHandler(BaseObjectCommandsGatewayHandler):
|
|
|
258
279
|
return
|
|
259
280
|
elif component.controller_uid == Blinds.uid:
|
|
260
281
|
self.control_blinds(component, payload.get('set_val'))
|
|
282
|
+
elif component.controller_uid == AlarmGroup.uid:
|
|
283
|
+
self.control_alarm_group(component, payload.get('set_val'))
|
|
284
|
+
elif component.controller_uid == Gate:
|
|
285
|
+
self.control_gate(component, payload.get('set_val'))
|
|
261
286
|
|
|
262
287
|
def start_script(self, component):
|
|
263
288
|
print("START SCRIPT %s" % str(component))
|
|
@@ -308,9 +333,11 @@ class GenericGatewayHandler(BaseObjectCommandsGatewayHandler):
|
|
|
308
333
|
open_switch = Component.objects.get(
|
|
309
334
|
pk=blinds.config['open_switch']
|
|
310
335
|
)
|
|
336
|
+
open_switch.prepare_controller()
|
|
311
337
|
close_switch = Component.objects.get(
|
|
312
338
|
pk=blinds.config['close_switch']
|
|
313
339
|
)
|
|
340
|
+
close_switch.prepare_controller()
|
|
314
341
|
except:
|
|
315
342
|
return
|
|
316
343
|
|
|
@@ -340,9 +367,70 @@ class GenericGatewayHandler(BaseObjectCommandsGatewayHandler):
|
|
|
340
367
|
|
|
341
368
|
self.blinds_runners[blinds.id].start()
|
|
342
369
|
|
|
370
|
+
def control_alarm_group(self, alarm_group, value):
|
|
371
|
+
from simo.generic.controllers import AlarmGroup
|
|
372
|
+
|
|
373
|
+
other_alarm_groups = {}
|
|
374
|
+
stats = {
|
|
375
|
+
'disarmed': 0, 'pending-arm': 0, 'armed': 0, 'breached': 0
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
for c_id in alarm_group.config['components']:
|
|
379
|
+
slave = Component.objects.filter(pk=c_id).first()
|
|
380
|
+
if not slave:
|
|
381
|
+
continue
|
|
382
|
+
if value == 'armed':
|
|
383
|
+
if not slave.is_in_alarm():
|
|
384
|
+
slave.arm_status = 'armed'
|
|
385
|
+
stats['armed'] += 1
|
|
386
|
+
else:
|
|
387
|
+
slave.arm_status = 'pending-arm'
|
|
388
|
+
stats['pending-arm'] += 1
|
|
389
|
+
elif value == 'disarmed':
|
|
390
|
+
stats['disarmed'] += 1
|
|
391
|
+
slave.arm_status = 'disarmed'
|
|
392
|
+
|
|
393
|
+
slave.do_not_update_alarm_group = True
|
|
394
|
+
slave.save(update_fields=['arm_status'])
|
|
395
|
+
|
|
396
|
+
for other_group in Component.objects.filter(
|
|
397
|
+
controller_uid=AlarmGroup.uid,
|
|
398
|
+
config__components__contains=slave.id
|
|
399
|
+
).exclude(pk=alarm_group.pk):
|
|
400
|
+
other_alarm_groups[other_group.pk] = other_group
|
|
401
|
+
|
|
402
|
+
alarm_group.value = value
|
|
403
|
+
if stats['pending-arm']:
|
|
404
|
+
alarm_group.value = 'pending-arm'
|
|
405
|
+
alarm_group.config['stats'] = stats
|
|
406
|
+
alarm_group.save()
|
|
407
|
+
|
|
408
|
+
for pk, other_group in other_alarm_groups.items():
|
|
409
|
+
other_group.prepare_controller()
|
|
410
|
+
other_group.refresh_status()
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
def control_gate(self, gate, value):
|
|
414
|
+
switch = Component.objects.filter(
|
|
415
|
+
pk=gate.config.get('action_switch')
|
|
416
|
+
).first()
|
|
417
|
+
if not switch:
|
|
418
|
+
return
|
|
419
|
+
switch.prepare_controller()
|
|
343
420
|
|
|
421
|
+
if gate.config.get('action_method') == 'click':
|
|
422
|
+
switch.click()
|
|
423
|
+
else:
|
|
424
|
+
if value == 'open':
|
|
425
|
+
switch.turn_on()
|
|
426
|
+
else:
|
|
427
|
+
switch.turn_off()
|
|
344
428
|
|
|
345
429
|
|
|
346
430
|
class DummyGatewayHandler(BaseObjectCommandsGatewayHandler):
|
|
347
431
|
name = "Dummy"
|
|
348
432
|
config_form = BaseGatewayForm
|
|
433
|
+
|
|
434
|
+
def perform_value_send(self, component, value):
|
|
435
|
+
component.controller.set(value)
|
|
436
|
+
|
simo/generic/models.py
CHANGED
|
@@ -63,9 +63,9 @@ def handle_alarm_groups(sender, instance, *args, **kwargs):
|
|
|
63
63
|
alarm_group.set(alarm_group_value)
|
|
64
64
|
|
|
65
65
|
|
|
66
|
-
@receiver(
|
|
67
|
-
def set_initial_alarm_group_stats(sender, instance, *args, **kwargs):
|
|
68
|
-
if
|
|
66
|
+
@receiver(post_save, sender=Component)
|
|
67
|
+
def set_initial_alarm_group_stats(sender, instance, created, *args, **kwargs):
|
|
68
|
+
if not created:
|
|
69
69
|
return
|
|
70
70
|
if instance.controller_uid != AlarmGroup.uid:
|
|
71
71
|
return
|
simo/multimedia/controllers.py
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
from django.utils.translation import gettext_lazy as _
|
|
2
2
|
from django.core.exceptions import ValidationError
|
|
3
3
|
from simo.core.controllers import BEFORE_SEND, BEFORE_SET
|
|
4
|
-
from simo.core.controllers import
|
|
4
|
+
from simo.core.controllers import Switch, TimerMixin
|
|
5
5
|
from .app_widgets import AudioPlayerWidget, VideoPlayerWidget
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
class BasePlayer(
|
|
8
|
+
class BasePlayer(Switch):
|
|
9
9
|
default_config = {
|
|
10
10
|
'has_volume_control': True,
|
|
11
11
|
}
|
|
@@ -22,6 +22,13 @@ class BasePlayer(ControllerBase, TimerMixin):
|
|
|
22
22
|
}
|
|
23
23
|
default_value = 'stopped'
|
|
24
24
|
|
|
25
|
+
def _prepare_for_send(self, value):
|
|
26
|
+
if isinstance(value, bool):
|
|
27
|
+
if value:
|
|
28
|
+
return 'play'
|
|
29
|
+
return 'pause'
|
|
30
|
+
return value
|
|
31
|
+
|
|
25
32
|
def _validate_val(self, value, occasion=None):
|
|
26
33
|
return value
|
|
27
34
|
|
|
@@ -59,12 +66,6 @@ class BasePlayer(ControllerBase, TimerMixin):
|
|
|
59
66
|
def play_library_item(self, val):
|
|
60
67
|
self.send({'play_from_library': val})
|
|
61
68
|
|
|
62
|
-
def turn_on(self):
|
|
63
|
-
self.play()
|
|
64
|
-
|
|
65
|
-
def turn_off(self):
|
|
66
|
-
self.pause()
|
|
67
|
-
|
|
68
69
|
def toggle(self):
|
|
69
70
|
if self.component.value == 'playing':
|
|
70
71
|
self.turn_off()
|
simo/settings.py
CHANGED
|
@@ -41,7 +41,6 @@ EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
|
|
|
41
41
|
|
|
42
42
|
INSTALLED_APPS = [
|
|
43
43
|
'channels',
|
|
44
|
-
'filebrowser',
|
|
45
44
|
'dal',
|
|
46
45
|
'dal_select2',
|
|
47
46
|
'django.forms',
|
|
@@ -87,8 +86,11 @@ MIDDLEWARE = [
|
|
|
87
86
|
'simo.core.middleware.instance_middleware'
|
|
88
87
|
]
|
|
89
88
|
|
|
89
|
+
|
|
90
|
+
FILE_UPLOAD_MAX_MEMORY_SIZE = 20971520 # 20Mb
|
|
91
|
+
|
|
90
92
|
ROOT_URLCONF = 'urls'
|
|
91
|
-
WSGI_APPLICATION = 'wsgi.application'
|
|
93
|
+
WSGI_APPLICATION = 'simo.wsgi.application'
|
|
92
94
|
|
|
93
95
|
CHANNELS_URLCONF = 'simo.asgi'
|
|
94
96
|
ASGI_APPLICATION = "asgi.application"
|
|
@@ -120,7 +122,7 @@ DATABASES = {
|
|
|
120
122
|
'default': {
|
|
121
123
|
'ENGINE': 'simo.core.db_backend',
|
|
122
124
|
'NAME': 'SIMO',
|
|
123
|
-
'ATOMIC_REQUESTS': False
|
|
125
|
+
'ATOMIC_REQUESTS': False,
|
|
124
126
|
}
|
|
125
127
|
}
|
|
126
128
|
|
|
@@ -185,7 +187,8 @@ REST_FRAMEWORK = {
|
|
|
185
187
|
'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'],
|
|
186
188
|
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
|
|
187
189
|
'PAGE_SIZE': 1000,
|
|
188
|
-
'DATETIME_FORMAT': '%s.%f'
|
|
190
|
+
'DATETIME_FORMAT': '%s.%f',
|
|
191
|
+
'DEFAULT_METADATA_CLASS': 'simo.core.api_meta.SIMOAPIMetadata'
|
|
189
192
|
}
|
|
190
193
|
|
|
191
194
|
REDIS_DB = {
|
simo/urls.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import importlib
|
|
2
2
|
import inspect
|
|
3
|
+
import sys
|
|
3
4
|
from django.urls import path, include
|
|
4
5
|
from django.conf.urls.static import static
|
|
5
6
|
from django.conf import settings
|
|
@@ -9,23 +10,17 @@ from django.apps import apps
|
|
|
9
10
|
from django.contrib.auth.views import LogoutView
|
|
10
11
|
from rest_framework import routers
|
|
11
12
|
from rest_framework.viewsets import GenericViewSet
|
|
12
|
-
from filebrowser.sites import FileBrowserSite
|
|
13
13
|
from django.contrib.admin import site as admin_site
|
|
14
14
|
from simo.users.views import protected_static
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
filebrowser_site = FileBrowserSite(
|
|
18
|
-
name='filebrowser', storage=FileSystemStorage(
|
|
19
|
-
settings.VAR_DIR, settings.VAR_DIR_URL
|
|
20
|
-
)
|
|
21
|
-
)
|
|
22
17
|
|
|
23
18
|
rest_router = routers.DefaultRouter()
|
|
24
19
|
registered_classes = []
|
|
25
20
|
for name, app in apps.app_configs.items():
|
|
26
21
|
try:
|
|
27
22
|
apis = importlib.import_module('%s.api' % app.name)
|
|
28
|
-
except ModuleNotFoundError:
|
|
23
|
+
except ModuleNotFoundError as e:
|
|
29
24
|
continue
|
|
30
25
|
for cls_name, cls in apis.__dict__.items():
|
|
31
26
|
cls_id = '%s.%s' % (app.name, cls_name)
|
|
@@ -39,7 +34,8 @@ for name, app in apps.app_configs.items():
|
|
|
39
34
|
urlpatterns = [
|
|
40
35
|
path('', RedirectView.as_view(pattern_name='admin:index')),
|
|
41
36
|
path('login/', include('simo.users.sso_urls')),
|
|
42
|
-
|
|
37
|
+
|
|
38
|
+
|
|
43
39
|
path('admin/login/',
|
|
44
40
|
RedirectView.as_view(
|
|
45
41
|
pattern_name='login', query_string=True
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|