simo 2.10.7__py3-none-any.whl → 2.10.11__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__/__init__.cpython-312.pyc +0 -0
- simo/__pycache__/asgi.cpython-312.pyc +0 -0
- simo/__pycache__/celeryc.cpython-312.pyc +0 -0
- simo/__pycache__/conf.cpython-312.pyc +0 -0
- simo/__pycache__/settings.cpython-312.pyc +0 -0
- simo/__pycache__/urls.cpython-312.pyc +0 -0
- simo/automation/__pycache__/__init__.cpython-312.pyc +0 -0
- simo/automation/__pycache__/app_widgets.cpython-312.pyc +0 -0
- simo/automation/__pycache__/controllers.cpython-312.pyc +0 -0
- simo/automation/__pycache__/forms.cpython-312.pyc +0 -0
- simo/automation/__pycache__/gateways.cpython-312.pyc +0 -0
- simo/automation/__pycache__/helpers.cpython-312.pyc +0 -0
- simo/automation/__pycache__/models.cpython-312.pyc +0 -0
- simo/automation/__pycache__/serializers.cpython-312.pyc +0 -0
- simo/automation/__pycache__/state.cpython-312.pyc +0 -0
- simo/automation/migrations/__pycache__/0001_initial.cpython-312.pyc +0 -0
- simo/automation/migrations/__pycache__/0002_update_helpers_in_scripts.cpython-312.pyc +0 -0
- simo/automation/migrations/__pycache__/__init__.cpython-312.pyc +0 -0
- simo/automation/templates/automations/__pycache__/auto_away.cpython-312.pyc +0 -0
- simo/automation/templates/automations/__pycache__/auto_state_script.cpython-312.pyc +0 -0
- simo/automation/templates/automations/__pycache__/phones_sleep_script.cpython-312.pyc +0 -0
- simo/backups/__pycache__/__init__.cpython-312.pyc +0 -0
- simo/backups/__pycache__/admin.cpython-312.pyc +0 -0
- simo/backups/__pycache__/dynamic_settings.cpython-312.pyc +0 -0
- simo/backups/__pycache__/models.cpython-312.pyc +0 -0
- simo/backups/__pycache__/tasks.cpython-312.pyc +0 -0
- simo/backups/migrations/__pycache__/0001_initial.cpython-312.pyc +0 -0
- simo/backups/migrations/__pycache__/0002_backuplog_backup_level_backup_size.cpython-312.pyc +0 -0
- simo/backups/migrations/__pycache__/0003_alter_backuplog_options_alter_backup_size.cpython-312.pyc +0 -0
- simo/backups/migrations/__pycache__/0004_alter_backup_options_alter_backuplog_options_and_more.cpython-312.pyc +0 -0
- simo/backups/migrations/__pycache__/__init__.cpython-312.pyc +0 -0
- simo/core/__pycache__/__init__.cpython-312.pyc +0 -0
- simo/core/__pycache__/admin.cpython-312.pyc +0 -0
- simo/core/__pycache__/api.cpython-312.pyc +0 -0
- simo/core/__pycache__/api_auth.cpython-312.pyc +0 -0
- simo/core/__pycache__/api_meta.cpython-312.pyc +0 -0
- simo/core/__pycache__/app_widgets.cpython-312.pyc +0 -0
- simo/core/__pycache__/apps.cpython-312.pyc +0 -0
- simo/core/__pycache__/auto_urls.cpython-312.pyc +0 -0
- simo/core/__pycache__/autocomplete_views.cpython-312.pyc +0 -0
- simo/core/__pycache__/base_types.cpython-312.pyc +0 -0
- simo/core/__pycache__/context.cpython-312.pyc +0 -0
- simo/core/__pycache__/controllers.cpython-312.pyc +0 -0
- simo/core/__pycache__/dynamic_settings.cpython-312.pyc +0 -0
- simo/core/__pycache__/events.cpython-312.pyc +0 -0
- simo/core/__pycache__/filters.cpython-312.pyc +0 -0
- simo/core/__pycache__/form_fields.cpython-312.pyc +0 -0
- simo/core/__pycache__/forms.cpython-312.pyc +0 -0
- simo/core/__pycache__/gateways.cpython-312.pyc +0 -0
- simo/core/__pycache__/loggers.cpython-312.pyc +0 -0
- simo/core/__pycache__/managers.cpython-312.pyc +0 -0
- simo/core/__pycache__/middleware.cpython-312.pyc +0 -0
- simo/core/__pycache__/models.cpython-312.pyc +0 -0
- simo/core/__pycache__/permissions.cpython-312.pyc +0 -0
- simo/core/__pycache__/routing.cpython-312.pyc +0 -0
- simo/core/__pycache__/serializers.cpython-312.pyc +0 -0
- simo/core/__pycache__/signal_receivers.cpython-312.pyc +0 -0
- simo/core/__pycache__/socket_consumers.cpython-312.pyc +0 -0
- simo/core/__pycache__/storage.cpython-312.pyc +0 -0
- simo/core/__pycache__/tasks.cpython-312.pyc +0 -0
- simo/core/__pycache__/todos.cpython-312.pyc +0 -0
- simo/core/__pycache__/types.cpython-312.pyc +0 -0
- simo/core/__pycache__/views.cpython-312.pyc +0 -0
- simo/core/__pycache__/widgets.cpython-312.pyc +0 -0
- simo/core/controllers.py +2 -2
- simo/core/db_backend/__pycache__/__init__.cpython-312.pyc +0 -0
- simo/core/db_backend/__pycache__/base.cpython-312.pyc +0 -0
- simo/core/drf_braces/__pycache__/__init__.cpython-312.pyc +0 -0
- simo/core/drf_braces/__pycache__/mixins.cpython-312.pyc +0 -0
- simo/core/drf_braces/__pycache__/models.cpython-312.pyc +0 -0
- simo/core/drf_braces/__pycache__/parsers.cpython-312.pyc +0 -0
- simo/core/drf_braces/__pycache__/renderers.cpython-312.pyc +0 -0
- simo/core/drf_braces/__pycache__/utils.cpython-312.pyc +0 -0
- simo/core/drf_braces/fields/__pycache__/__init__.cpython-312.pyc +0 -0
- simo/core/drf_braces/fields/__pycache__/_fields.cpython-312.pyc +0 -0
- simo/core/drf_braces/fields/__pycache__/custom.cpython-312.pyc +0 -0
- simo/core/drf_braces/fields/__pycache__/mixins.cpython-312.pyc +0 -0
- simo/core/drf_braces/fields/__pycache__/modified.cpython-312.pyc +0 -0
- simo/core/drf_braces/forms/__pycache__/__init__.cpython-312.pyc +0 -0
- simo/core/drf_braces/forms/__pycache__/fields.cpython-312.pyc +0 -0
- simo/core/drf_braces/forms/__pycache__/serializer_form.cpython-312.pyc +0 -0
- simo/core/drf_braces/serializers/__pycache__/__init__.cpython-312.pyc +0 -0
- simo/core/drf_braces/serializers/__pycache__/enforce_validation_serializer.cpython-312.pyc +0 -0
- simo/core/drf_braces/serializers/__pycache__/form_serializer.cpython-312.pyc +0 -0
- simo/core/drf_braces/serializers/__pycache__/swapping.cpython-312.pyc +0 -0
- simo/core/drf_braces/tests/__pycache__/__init__.cpython-312.pyc +0 -0
- simo/core/drf_braces/tests/__pycache__/test_mixins.cpython-312.pyc +0 -0
- simo/core/drf_braces/tests/__pycache__/test_parsers.cpython-312.pyc +0 -0
- simo/core/drf_braces/tests/__pycache__/test_renderers.cpython-312.pyc +0 -0
- simo/core/drf_braces/tests/__pycache__/test_utils.cpython-312.pyc +0 -0
- simo/core/drf_braces/tests/fields/__pycache__/__init__.cpython-312.pyc +0 -0
- simo/core/drf_braces/tests/fields/__pycache__/test_custom.cpython-312.pyc +0 -0
- simo/core/drf_braces/tests/fields/__pycache__/test_fields.cpython-312.pyc +0 -0
- simo/core/drf_braces/tests/fields/__pycache__/test_mixins.cpython-312.pyc +0 -0
- simo/core/drf_braces/tests/fields/__pycache__/test_modified.cpython-312.pyc +0 -0
- simo/core/drf_braces/tests/forms/__pycache__/__init__.cpython-312.pyc +0 -0
- simo/core/drf_braces/tests/forms/__pycache__/test_fields.cpython-312.pyc +0 -0
- simo/core/drf_braces/tests/forms/__pycache__/test_serializer_form.cpython-312.pyc +0 -0
- simo/core/drf_braces/tests/serializers/__pycache__/__init__.cpython-312.pyc +0 -0
- simo/core/drf_braces/tests/serializers/__pycache__/test_enforce_validation_serializer.cpython-312.pyc +0 -0
- simo/core/drf_braces/tests/serializers/__pycache__/test_form_serializer.cpython-312.pyc +0 -0
- simo/core/drf_braces/tests/serializers/__pycache__/test_swapping.cpython-312.pyc +0 -0
- simo/core/management/__pycache__/__init__.cpython-312.pyc +0 -0
- simo/core/management/__pycache__/update.cpython-312.pyc +0 -0
- simo/core/management/_hub_template/hub/__pycache__/asgi.cpython-312.pyc +0 -0
- simo/core/management/_hub_template/hub/__pycache__/celeryc.cpython-312.pyc +0 -0
- simo/core/management/_hub_template/hub/__pycache__/manage.cpython-312.pyc +0 -0
- simo/core/management/_hub_template/hub/__pycache__/settings.cpython-312.pyc +0 -0
- simo/core/management/_hub_template/hub/__pycache__/urls.cpython-312.pyc +0 -0
- simo/core/management/_hub_template/hub/__pycache__/wsgi.cpython-312.pyc +0 -0
- simo/core/management/commands/__pycache__/__init__.cpython-312.pyc +0 -0
- simo/core/management/commands/__pycache__/gateways_manager.cpython-312.pyc +0 -0
- simo/core/management/commands/__pycache__/on_http_start.cpython-312.pyc +0 -0
- simo/core/management/commands/__pycache__/run_gateway.cpython-312.pyc +0 -0
- simo/core/migrations/0050_componenthistory_alive.py +18 -0
- simo/core/migrations/__pycache__/0001_initial.cpython-312.pyc +0 -0
- simo/core/migrations/__pycache__/0002_load_icons.cpython-312.pyc +0 -0
- simo/core/migrations/__pycache__/0003_create_default_zones_and_categories.cpython-312.pyc +0 -0
- simo/core/migrations/__pycache__/0004_create_generic.cpython-312.pyc +0 -0
- simo/core/migrations/__pycache__/0005_component_subcomponents.cpython-312.pyc +0 -0
- simo/core/migrations/__pycache__/0006_alter_component_subcomponents.cpython-312.pyc +0 -0
- simo/core/migrations/__pycache__/0007_component_change_init_to.cpython-312.pyc +0 -0
- simo/core/migrations/__pycache__/0008_alter_component_change_init_to.cpython-312.pyc +0 -0
- simo/core/migrations/__pycache__/0009_auto_20220707_1404.cpython-312.pyc +0 -0
- simo/core/migrations/__pycache__/0010_historyaggregate.cpython-312.pyc +0 -0
- simo/core/migrations/__pycache__/0011_component_last_change.cpython-312.pyc +0 -0
- simo/core/migrations/__pycache__/0012_instance.cpython-312.pyc +0 -0
- simo/core/migrations/__pycache__/0013_auto_20231003_0754.cpython-312.pyc +0 -0
- simo/core/migrations/__pycache__/0014_zone_instance.cpython-312.pyc +0 -0
- simo/core/migrations/__pycache__/0015_auto_20231004_1113.cpython-312.pyc +0 -0
- simo/core/migrations/__pycache__/0016_auto_20231004_1113.cpython-312.pyc +0 -0
- simo/core/migrations/__pycache__/0017_auto_20231004_1313.cpython-312.pyc +0 -0
- simo/core/migrations/__pycache__/0018_auto_20231005_0622.cpython-312.pyc +0 -0
- simo/core/migrations/__pycache__/0019_alter_gateway_type.cpython-312.pyc +0 -0
- simo/core/migrations/__pycache__/0020_component_meta.cpython-312.pyc +0 -0
- simo/core/migrations/__pycache__/0021_auto_20231020_1041.cpython-312.pyc +0 -0
- simo/core/migrations/__pycache__/0022_auto_20231221_0735.cpython-312.pyc +0 -0
- simo/core/migrations/__pycache__/0023_auto_20231229_1352.cpython-312.pyc +0 -0
- simo/core/migrations/__pycache__/0024_alter_instance_device_report_history_days.cpython-312.pyc +0 -0
- simo/core/migrations/__pycache__/0025_auto_20240122_1321.cpython-312.pyc +0 -0
- simo/core/migrations/__pycache__/0026_category_instance.cpython-312.pyc +0 -0
- simo/core/migrations/__pycache__/0027_remove_component_tags.cpython-312.pyc +0 -0
- simo/core/migrations/__pycache__/0028_rename_subcomponents_component_slaves.cpython-312.pyc +0 -0
- simo/core/migrations/__pycache__/0029_auto_20240229_1331.cpython-312.pyc +0 -0
- simo/core/migrations/__pycache__/0030_alter_instance_timezone.cpython-312.pyc +0 -0
- simo/core/migrations/__pycache__/0031_auto_20240429_1231.cpython-312.pyc +0 -0
- simo/core/migrations/__pycache__/0032_auto_20240506_0834.cpython-312.pyc +0 -0
- simo/core/migrations/__pycache__/0033_auto_20240509_0821.cpython-312.pyc +0 -0
- simo/core/migrations/__pycache__/0034_component_error_msg.cpython-312.pyc +0 -0
- simo/core/migrations/__pycache__/0035_remove_instance_share_location.cpython-312.pyc +0 -0
- simo/core/migrations/__pycache__/0036_auto_20240521_0823.cpython-312.pyc +0 -0
- simo/core/migrations/__pycache__/0037_auto_20240606_1057.cpython-312.pyc +0 -0
- simo/core/migrations/__pycache__/0038_remove_instance_cover_image_and_more.cpython-312.pyc +0 -0
- simo/core/migrations/__pycache__/0039_instance_is_active_alter_instance_timezone.cpython-312.pyc +0 -0
- simo/core/migrations/__pycache__/0040_alter_instance_name.cpython-312.pyc +0 -0
- simo/core/migrations/__pycache__/0041_alter_instance_slug.cpython-312.pyc +0 -0
- simo/core/migrations/__pycache__/0042_alter_instance_timezone.cpython-312.pyc +0 -0
- simo/core/migrations/__pycache__/0043_alter_category_instance_alter_instance_timezone_and_more.cpython-312.pyc +0 -0
- simo/core/migrations/__pycache__/0044_alter_gateway_type.cpython-312.pyc +0 -0
- simo/core/migrations/__pycache__/0045_alter_instance_device_report_history_days_and_more.cpython-312.pyc +0 -0
- simo/core/migrations/__pycache__/0046_component_value_translation_alter_gateway_type.cpython-312.pyc +0 -0
- simo/core/migrations/__pycache__/0047_alter_component_value_translation.cpython-312.pyc +0 -0
- simo/core/migrations/__pycache__/0048_publicfile_privatefile.cpython-312.pyc +0 -0
- simo/core/migrations/__pycache__/0049_alter_gateway_type.cpython-312.pyc +0 -0
- simo/core/migrations/__pycache__/0050_componenthistory_alive.cpython-312.pyc +0 -0
- simo/core/migrations/__pycache__/__init__.cpython-312.pyc +0 -0
- simo/core/models.py +3 -1
- simo/core/serializers.py +2 -2
- simo/core/tasks.py +0 -1
- simo/core/templates/admin/component_history.html +18 -2
- simo/core/templates/core/__pycache__/value_translation.cpython-312.pyc +0 -0
- simo/core/templatetags/__pycache__/__init__.cpython-312.pyc +0 -0
- simo/core/templatetags/__pycache__/components_list.cpython-312.pyc +0 -0
- simo/core/utils/__pycache__/__init__.cpython-312.pyc +0 -0
- simo/core/utils/__pycache__/admin.cpython-312.pyc +0 -0
- simo/core/utils/__pycache__/api.cpython-312.pyc +0 -0
- simo/core/utils/__pycache__/cache.cpython-312.pyc +0 -0
- simo/core/utils/__pycache__/config_values.cpython-312.pyc +0 -0
- simo/core/utils/__pycache__/converters.cpython-312.pyc +0 -0
- simo/core/utils/__pycache__/easing.cpython-312.pyc +0 -0
- simo/core/utils/__pycache__/form_fields.cpython-312.pyc +0 -0
- simo/core/utils/__pycache__/form_widgets.cpython-312.pyc +0 -0
- simo/core/utils/__pycache__/formsets.cpython-312.pyc +0 -0
- simo/core/utils/__pycache__/helpers.cpython-312.pyc +0 -0
- simo/core/utils/__pycache__/json.cpython-312.pyc +0 -0
- simo/core/utils/__pycache__/logs.cpython-312.pyc +0 -0
- simo/core/utils/__pycache__/mixins.cpython-312.pyc +0 -0
- simo/core/utils/__pycache__/model_helpers.cpython-312.pyc +0 -0
- simo/core/utils/__pycache__/operations.cpython-312.pyc +0 -0
- simo/core/utils/__pycache__/relay.cpython-312.pyc +0 -0
- simo/core/utils/__pycache__/serialization.cpython-312.pyc +0 -0
- simo/core/utils/__pycache__/type_constants.cpython-312.pyc +0 -0
- simo/core/utils/__pycache__/validators.cpython-312.pyc +0 -0
- simo/fleet/__pycache__/__init__.cpython-312.pyc +0 -0
- simo/fleet/__pycache__/admin.cpython-312.pyc +0 -0
- simo/fleet/__pycache__/api.cpython-312.pyc +0 -0
- simo/fleet/__pycache__/apps.cpython-312.pyc +0 -0
- simo/fleet/__pycache__/auto_urls.cpython-312.pyc +0 -0
- simo/fleet/__pycache__/base_types.cpython-312.pyc +0 -0
- simo/fleet/__pycache__/ble.cpython-312.pyc +0 -0
- simo/fleet/__pycache__/controllers.cpython-312.pyc +0 -0
- simo/fleet/__pycache__/custom_dali_operations.cpython-312.pyc +0 -0
- simo/fleet/__pycache__/forms.cpython-312.pyc +0 -0
- simo/fleet/__pycache__/gateways.cpython-312.pyc +0 -0
- simo/fleet/__pycache__/managers.cpython-312.pyc +0 -0
- simo/fleet/__pycache__/models.cpython-312.pyc +0 -0
- simo/fleet/__pycache__/routing.cpython-312.pyc +0 -0
- simo/fleet/__pycache__/serializers.cpython-312.pyc +0 -0
- simo/fleet/__pycache__/socket_consumers.cpython-312.pyc +0 -0
- simo/fleet/__pycache__/tasks.cpython-312.pyc +0 -0
- simo/fleet/__pycache__/utils.cpython-312.pyc +0 -0
- simo/fleet/__pycache__/views.cpython-312.pyc +0 -0
- simo/fleet/api.py +5 -2
- simo/fleet/controllers.py +136 -38
- simo/fleet/forms.py +177 -207
- simo/fleet/gateways.py +34 -4
- simo/fleet/migrations/0052_colonelpin_interface.py +18 -0
- simo/fleet/migrations/0053_auto_20250507_0713.py +24 -0
- simo/fleet/migrations/0054_auto_20250507_1256.py +36 -0
- simo/fleet/migrations/__pycache__/0001_initial.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0002_auto_20220422_0743.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0003_auto_20220422_0752.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0004_auto_20220422_0818.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0005_auto_20220428_0900.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0006_rename_mac_colonel_uid.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0007_colonel_socket_connected.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0008_i2cinterface.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0009_i2cinterface_name.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0010_auto_20220602_0746.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0011_i2cinterface_freq.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0012_colonel_logs_stream.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0013_alter_colonel_last_seen.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0014_auto_20220614_0659.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0015_auto_20220614_0754.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0016_auto_20220704_0840.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0017_alter_colonel_secret.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0018_colonel_instance.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0019_auto_20231006_0749.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0020_instanceoptions.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0021_auto_20231006_0819.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0022_remove_colonel_secret.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0023_colonel_is_authorized.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0024_colonel_pwm_frequency.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0025_auto_20240130_1334.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0026_rename_i2cinterface_scl_pin_and_more.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0027_auto_20240306_0802.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0028_remove_i2cinterface_scl_pin_no_and_more.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0029_alter_i2cinterface_scl_pin_and_more.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0030_colonelpin_label_alter_colonel_type.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0031_alter_colonel_type.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0032_auto_20240415_0736.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0033_auto_20240415_0736.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0034_auto_20240418_0735.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0035_auto_20240514_0855.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0036_auto_20240605_0702.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0037_alter_colonelpin_options_alter_colonelpin_no_and_more.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0038_alter_colonel_type.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0039_auto_20241016_1047.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0040_alter_colonel_pwm_frequency.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0041_alter_colonel_instance_and_more.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0042_auto_20241120_1028.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0043_auto_20241203_0930.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0044_auto_20241210_0707.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0045_alter_colonel_type_customdalidevice.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0046_delete_customdalidevice.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0047_customdalidevice.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0048_remove_customdalidevice_colonel_and_more.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0049_alter_customdalidevice_interface.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0050_customdalidevice_uid.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0051_customdalidevice_components.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0052_colonelpin_interface.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0053_auto_20250507_0713.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0054_auto_20250507_1256.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/__init__.cpython-312.pyc +0 -0
- simo/fleet/models.py +96 -34
- simo/fleet/serializers.py +2 -2
- simo/fleet/utils.py +126 -0
- simo/generic/__pycache__/__init__.cpython-312.pyc +0 -0
- simo/generic/__pycache__/app_widgets.cpython-312.pyc +0 -0
- simo/generic/__pycache__/base_types.cpython-312.pyc +0 -0
- simo/generic/__pycache__/controllers.cpython-312.pyc +0 -0
- simo/generic/__pycache__/forms.cpython-312.pyc +0 -0
- simo/generic/__pycache__/gateways.cpython-312.pyc +0 -0
- simo/generic/__pycache__/models.cpython-312.pyc +0 -0
- simo/generic/__pycache__/routing.cpython-312.pyc +0 -0
- simo/generic/__pycache__/socket_consumers.cpython-312.pyc +0 -0
- simo/generic/__pycache__/tasks.cpython-312.pyc +0 -0
- simo/generic/migrations/__pycache__/0001_initial.cpython-312.pyc +0 -0
- simo/generic/migrations/__pycache__/0002_auto_20241126_0726.cpython-312.pyc +0 -0
- simo/generic/migrations/__pycache__/0003_auto_20250409_1404.cpython-312.pyc +0 -0
- simo/generic/migrations/__pycache__/__init__.cpython-312.pyc +0 -0
- simo/multimedia/__pycache__/__init__.cpython-312.pyc +0 -0
- simo/multimedia/__pycache__/admin.cpython-312.pyc +0 -0
- simo/multimedia/__pycache__/api.cpython-312.pyc +0 -0
- simo/multimedia/__pycache__/app_widgets.cpython-312.pyc +0 -0
- simo/multimedia/__pycache__/auto_urls.cpython-312.pyc +0 -0
- simo/multimedia/__pycache__/base_types.cpython-312.pyc +0 -0
- simo/multimedia/__pycache__/controllers.cpython-312.pyc +0 -0
- simo/multimedia/__pycache__/forms.cpython-312.pyc +0 -0
- simo/multimedia/__pycache__/models.cpython-312.pyc +0 -0
- simo/multimedia/__pycache__/serializers.cpython-312.pyc +0 -0
- simo/multimedia/__pycache__/views.cpython-312.pyc +0 -0
- simo/multimedia/migrations/__pycache__/0001_initial.cpython-312.pyc +0 -0
- simo/multimedia/migrations/__pycache__/0002_sound_length.cpython-312.pyc +0 -0
- simo/multimedia/migrations/__pycache__/0003_alter_sound_length.cpython-312.pyc +0 -0
- simo/multimedia/migrations/__pycache__/0004_auto_20231023_1055.cpython-312.pyc +0 -0
- simo/multimedia/migrations/__pycache__/0005_remove_sound_slug_sound_date_uploaded.cpython-312.pyc +0 -0
- simo/multimedia/migrations/__pycache__/0006_remove_sound_length_sound_duration.cpython-312.pyc +0 -0
- simo/multimedia/migrations/__pycache__/__init__.cpython-312.pyc +0 -0
- simo/notifications/__pycache__/__init__.cpython-312.pyc +0 -0
- simo/notifications/__pycache__/admin.cpython-312.pyc +0 -0
- simo/notifications/__pycache__/api.cpython-312.pyc +0 -0
- simo/notifications/__pycache__/models.cpython-312.pyc +0 -0
- simo/notifications/__pycache__/serializers.cpython-312.pyc +0 -0
- simo/notifications/__pycache__/utils.cpython-312.pyc +0 -0
- simo/notifications/migrations/__pycache__/0001_initial.cpython-312.pyc +0 -0
- simo/notifications/migrations/__pycache__/0002_notification_instance.cpython-312.pyc +0 -0
- simo/notifications/migrations/__pycache__/0003_alter_notification_instance.cpython-312.pyc +0 -0
- simo/notifications/migrations/__pycache__/__init__.cpython-312.pyc +0 -0
- simo/notifications/serializers.py +1 -1
- simo/users/__pycache__/__init__.cpython-312.pyc +0 -0
- simo/users/__pycache__/admin.cpython-312.pyc +0 -0
- simo/users/__pycache__/api.cpython-312.pyc +0 -0
- simo/users/__pycache__/apps.cpython-312.pyc +0 -0
- simo/users/__pycache__/auth_backends.cpython-312.pyc +0 -0
- simo/users/__pycache__/auto_urls.cpython-312.pyc +0 -0
- simo/users/__pycache__/dynamic_settings.cpython-312.pyc +0 -0
- simo/users/__pycache__/managers.cpython-312.pyc +0 -0
- simo/users/__pycache__/middleware.cpython-312.pyc +0 -0
- simo/users/__pycache__/models.cpython-312.pyc +0 -0
- simo/users/__pycache__/permissions.cpython-312.pyc +0 -0
- simo/users/__pycache__/serializers.cpython-312.pyc +0 -0
- simo/users/__pycache__/sso_urls.cpython-312.pyc +0 -0
- simo/users/__pycache__/sso_views.cpython-312.pyc +0 -0
- simo/users/__pycache__/tasks.cpython-312.pyc +0 -0
- simo/users/__pycache__/utils.cpython-312.pyc +0 -0
- simo/users/__pycache__/views.cpython-312.pyc +0 -0
- simo/users/migrations/__pycache__/0001_initial.cpython-312.pyc +0 -0
- simo/users/migrations/__pycache__/0002_componentpermission.cpython-312.pyc +0 -0
- simo/users/migrations/__pycache__/0003_create_roles_and_system_user.cpython-312.pyc +0 -0
- simo/users/migrations/__pycache__/0004_user_secret_key.cpython-312.pyc +0 -0
- simo/users/migrations/__pycache__/0005_permissionsrole_instance.cpython-312.pyc +0 -0
- simo/users/migrations/__pycache__/0006_auto_20231003_0850.cpython-312.pyc +0 -0
- simo/users/migrations/__pycache__/0007_auto_20231003_1228.cpython-312.pyc +0 -0
- simo/users/migrations/__pycache__/0008_auto_20231003_1229.cpython-312.pyc +0 -0
- simo/users/migrations/__pycache__/0009_remove_user_role.cpython-312.pyc +0 -0
- simo/users/migrations/__pycache__/0010_auto_20231004_1313.cpython-312.pyc +0 -0
- simo/users/migrations/__pycache__/0011_auto_20231004_1313.cpython-312.pyc +0 -0
- simo/users/migrations/__pycache__/0012_alter_userinstancerole_unique_together.cpython-312.pyc +0 -0
- simo/users/migrations/__pycache__/0013_remove_user_roles.cpython-312.pyc +0 -0
- simo/users/migrations/__pycache__/0014_user_roles.cpython-312.pyc +0 -0
- simo/users/migrations/__pycache__/0015_remove_user_at_home.cpython-312.pyc +0 -0
- simo/users/migrations/__pycache__/0016_auto_20231005_1050.cpython-312.pyc +0 -0
- simo/users/migrations/__pycache__/0017_auto_20231221_0735.cpython-312.pyc +0 -0
- simo/users/migrations/__pycache__/0018_user_is_god.cpython-312.pyc +0 -0
- simo/users/migrations/__pycache__/0019_auto_20231221_1155.cpython-312.pyc +0 -0
- simo/users/migrations/__pycache__/0020_rename_is_god_user_is_master.cpython-312.pyc +0 -0
- simo/users/migrations/__pycache__/0021_alter_permissionsrole_instance.cpython-312.pyc +0 -0
- simo/users/migrations/__pycache__/0022_userdevicereportlog_instance.cpython-312.pyc +0 -0
- simo/users/migrations/__pycache__/0023_auto_20240105_0719.cpython-312.pyc +0 -0
- simo/users/migrations/__pycache__/0024_fingerprint.cpython-312.pyc +0 -0
- simo/users/migrations/__pycache__/0025_rename_name_fingerprint_type_and_more.cpython-312.pyc +0 -0
- simo/users/migrations/__pycache__/0026_fingerprint_name.cpython-312.pyc +0 -0
- simo/users/migrations/__pycache__/0027_permissionsrole_can_manage_components.cpython-312.pyc +0 -0
- simo/users/migrations/__pycache__/0028_auto_20240506_1146.cpython-312.pyc +0 -0
- simo/users/migrations/__pycache__/0029_alter_instanceuser_instance.cpython-312.pyc +0 -0
- simo/users/migrations/__pycache__/0030_userdevice_users.cpython-312.pyc +0 -0
- simo/users/migrations/__pycache__/0031_auto_20240923_1115.cpython-312.pyc +0 -0
- simo/users/migrations/__pycache__/0032_remove_userdevice_user_alter_userdevice_users.cpython-312.pyc +0 -0
- simo/users/migrations/__pycache__/0033_alter_user_ssh_key.cpython-312.pyc +0 -0
- simo/users/migrations/__pycache__/0034_instanceuser_last_seen_location_and_more.cpython-312.pyc +0 -0
- simo/users/migrations/__pycache__/0035_instanceuser_last_seen_speed_kmh_and_more.cpython-312.pyc +0 -0
- simo/users/migrations/__pycache__/0036_instanceuser_phone_on_charge_user_phone_on_charge.cpython-312.pyc +0 -0
- simo/users/migrations/__pycache__/0037_rename_last_seen_location_datetime_instanceuser_last_seen_and_more.cpython-312.pyc +0 -0
- simo/users/migrations/__pycache__/0038_userdevicereportlog_at_home_and_more.cpython-312.pyc +0 -0
- simo/users/migrations/__pycache__/0039_auto_20241117_1039.cpython-312.pyc +0 -0
- simo/users/migrations/__pycache__/0040_userdevicereportlog_location_smoothed_and_more.cpython-312.pyc +0 -0
- simo/users/migrations/__pycache__/0041_userdevicereportlog_speed_kmh_received.cpython-312.pyc +0 -0
- simo/users/migrations/__pycache__/0042_remove_userdevicereportlog_location_smoothed_and_more.cpython-312.pyc +0 -0
- simo/users/migrations/__pycache__/0043_userdevicereportlog_avg_speed_kmh.cpython-312.pyc +0 -0
- simo/users/migrations/__pycache__/0044_permissionsrole_is_person.cpython-312.pyc +0 -0
- simo/users/migrations/__pycache__/__init__.cpython-312.pyc +0 -0
- {simo-2.10.7.dist-info → simo-2.10.11.dist-info}/METADATA +1 -1
- {simo-2.10.7.dist-info → simo-2.10.11.dist-info}/RECORD +388 -338
- {simo-2.10.7.dist-info → simo-2.10.11.dist-info}/WHEEL +1 -1
- {simo-2.10.7.dist-info → simo-2.10.11.dist-info}/entry_points.txt +0 -0
- {simo-2.10.7.dist-info → simo-2.10.11.dist-info}/licenses/LICENSE.md +0 -0
- {simo-2.10.7.dist-info → simo-2.10.11.dist-info}/top_level.txt +0 -0
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
simo/fleet/migrations/__pycache__/0028_remove_i2cinterface_scl_pin_no_and_more.cpython-312.pyc
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
simo/fleet/migrations/__pycache__/0048_remove_customdalidevice_colonel_and_more.cpython-312.pyc
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
simo/fleet/models.py
CHANGED
|
@@ -16,7 +16,11 @@ from simo.core.models import Instance, Gateway, Component
|
|
|
16
16
|
from simo.core.utils.helpers import get_random_string
|
|
17
17
|
from simo.core.events import GatewayObjectCommand
|
|
18
18
|
from .managers import ColonelsManager, ColonelPinsManager, InterfacesManager
|
|
19
|
-
|
|
19
|
+
# Now imported from utils
|
|
20
|
+
from .utils import GPIO_PINS, INTERFACES_PINS_MAP, \
|
|
21
|
+
_sync_interface_address_occupancy, _release_interface_addresses
|
|
22
|
+
|
|
23
|
+
# -------------------------------------------------------------------------
|
|
20
24
|
|
|
21
25
|
|
|
22
26
|
|
|
@@ -219,6 +223,7 @@ class ColonelPin(models.Model):
|
|
|
219
223
|
capacitive = models.BooleanField(default=False, db_index=True)
|
|
220
224
|
adc = models.BooleanField(default=False)
|
|
221
225
|
native = models.BooleanField(default=True, db_index=True)
|
|
226
|
+
interface = models.PositiveIntegerField(null=True, blank=True)
|
|
222
227
|
default_pull = models.CharField(
|
|
223
228
|
max_length=50, db_index=True, null=True, blank=True,
|
|
224
229
|
choices=(('LOW', "LOW"), ("HIGH", "HIGH"))
|
|
@@ -246,6 +251,12 @@ class ColonelPin(models.Model):
|
|
|
246
251
|
if not self.label:
|
|
247
252
|
# Might be created via migration...
|
|
248
253
|
self.save()
|
|
254
|
+
if self.interface:
|
|
255
|
+
interface = Interface.objects.filter(
|
|
256
|
+
colonel=self.colonel, no=self.interface
|
|
257
|
+
).first()
|
|
258
|
+
if interface and interface.type:
|
|
259
|
+
return f"{self.label} - {interface.get_type_display()}"
|
|
249
260
|
return self.label
|
|
250
261
|
|
|
251
262
|
def save(self, *args, **kwargs):
|
|
@@ -268,7 +279,8 @@ def after_colonel_save(sender, instance, created, *args, **kwargs):
|
|
|
268
279
|
defaults={
|
|
269
280
|
'input': data.get('input'), 'output': data.get('output'),
|
|
270
281
|
'capacitive': data.get('capacitive'), 'adc': data.get('adc'),
|
|
271
|
-
'native': data.get('native'), 'note': data.get('note')
|
|
282
|
+
'native': data.get('native'), 'note': data.get('note'),
|
|
283
|
+
'interface': data.get('interface')
|
|
272
284
|
}
|
|
273
285
|
)
|
|
274
286
|
fleet_gateway, new = Gateway.objects.get_or_create(
|
|
@@ -276,15 +288,6 @@ def after_colonel_save(sender, instance, created, *args, **kwargs):
|
|
|
276
288
|
)
|
|
277
289
|
if fleet_gateway.status != 'running':
|
|
278
290
|
fleet_gateway.start()
|
|
279
|
-
# create i2c and dali interfaces automatically for game-changer boards
|
|
280
|
-
if instance.type == 'game-changer':
|
|
281
|
-
# occupy ports immediately
|
|
282
|
-
Interface.objects.create(colonel=instance, no=1, type='i2c')
|
|
283
|
-
Interface.objects.create(colonel=instance, no=2, type='dali')
|
|
284
|
-
elif instance.type == 'game-changer-mini':
|
|
285
|
-
# only create interfaces, but do not occupy ports
|
|
286
|
-
Interface.objects.create(colonel=instance, no=1)
|
|
287
|
-
Interface.objects.create(colonel=instance, no=2)
|
|
288
291
|
|
|
289
292
|
if 'socket_connected' in instance.get_dirty_fields():
|
|
290
293
|
if instance.socket_connected:
|
|
@@ -308,15 +311,26 @@ def post_component_save(sender, instance, created, *args, **kwargs):
|
|
|
308
311
|
if not colonel:
|
|
309
312
|
return
|
|
310
313
|
colonel.components.add(instance)
|
|
314
|
+
|
|
315
|
+
# ------------------------------------------------------------------
|
|
316
|
+
# InterfaceAddress occupancy sync (always run for fleet components)
|
|
317
|
+
# ------------------------------------------------------------------
|
|
318
|
+
try:
|
|
319
|
+
_sync_interface_address_occupancy(instance)
|
|
320
|
+
except ValidationError:
|
|
321
|
+
raise
|
|
322
|
+
|
|
311
323
|
from .controllers import (
|
|
312
324
|
TTLock, DALILamp, DALIGearGroup, DALIRelay, DALIOccupancySensor,
|
|
313
325
|
DALILightSensor, DALIButton,
|
|
314
|
-
AirQualitySensor, TempHumSensor, AmbientLightSensor,
|
|
326
|
+
AirQualitySensor, TempHumSensor, AmbientLightSensor,
|
|
327
|
+
RoomPresenceSensor, RoomZonePresenceSensor
|
|
315
328
|
)
|
|
316
329
|
if instance.controller and instance.controller_cls in (
|
|
317
330
|
TTLock, DALILamp, DALIGearGroup, DALIRelay, DALIOccupancySensor,
|
|
318
331
|
DALILightSensor, DALIButton,
|
|
319
|
-
AirQualitySensor, TempHumSensor, AmbientLightSensor,
|
|
332
|
+
AirQualitySensor, TempHumSensor, AmbientLightSensor,
|
|
333
|
+
RoomPresenceSensor, RoomZonePresenceSensor
|
|
320
334
|
):
|
|
321
335
|
return
|
|
322
336
|
colonel.rebuild_occupied_pins()
|
|
@@ -328,6 +342,9 @@ def post_component_save(sender, instance, created, *args, **kwargs):
|
|
|
328
342
|
@receiver(pre_delete, sender=Component)
|
|
329
343
|
def post_component_delete(sender, instance, *args, **kwargs):
|
|
330
344
|
if not instance.controller_uid.startswith('simo.fleet'):
|
|
345
|
+
# Still ensure we release any InterfaceAddress occupied by this
|
|
346
|
+
# component to avoid dangling references.
|
|
347
|
+
_release_interface_addresses(instance)
|
|
331
348
|
return
|
|
332
349
|
|
|
333
350
|
from .controllers import DALIGearGroup
|
|
@@ -359,6 +376,9 @@ def post_component_delete(sender, instance, *args, **kwargs):
|
|
|
359
376
|
|
|
360
377
|
transaction.on_commit(update_colonel)
|
|
361
378
|
|
|
379
|
+
# Finally release any InterfaceAddress rows occupied by this component
|
|
380
|
+
_release_interface_addresses(instance)
|
|
381
|
+
|
|
362
382
|
|
|
363
383
|
class Interface(models.Model):
|
|
364
384
|
colonel = models.ForeignKey(
|
|
@@ -393,27 +413,48 @@ class Interface(models.Model):
|
|
|
393
413
|
def save(self, *args, **kwargs):
|
|
394
414
|
if not self.pin_a:
|
|
395
415
|
self.pin_a = ColonelPin.objects.get(
|
|
396
|
-
colonel=self.colonel,
|
|
416
|
+
colonel=self.colonel,
|
|
417
|
+
no=INTERFACES_PINS_MAP[self.no][0],
|
|
397
418
|
)
|
|
398
419
|
if not self.pin_b:
|
|
399
420
|
self.pin_b = ColonelPin.objects.get(
|
|
400
|
-
colonel=self.colonel,
|
|
421
|
+
colonel=self.colonel,
|
|
422
|
+
no=INTERFACES_PINS_MAP[self.no][1],
|
|
401
423
|
)
|
|
402
|
-
if self.type:
|
|
403
|
-
for pin_no in INTERFACES_PINS_MAP[self.no]:
|
|
404
|
-
cpin = ColonelPin.objects.get(colonel=self.colonel, no=pin_no)
|
|
405
|
-
if cpin.occupied_by and cpin.occupied_by != self:
|
|
406
|
-
raise ValidationError(
|
|
407
|
-
f"Interface can not be created, because "
|
|
408
|
-
f"{cpin} is already occupied by {cpin.occupied_by}."
|
|
409
|
-
)
|
|
410
|
-
self.pin_a.occupied_by = self
|
|
411
|
-
self.pin_b.occupied_by = self
|
|
412
|
-
else:
|
|
413
|
-
self.pin_a.occupied_by = None
|
|
414
|
-
self.pin_b.occupied_by = None
|
|
415
424
|
|
|
416
|
-
|
|
425
|
+
with transaction.atomic():
|
|
426
|
+
created = self.pk is None
|
|
427
|
+
super_save = super().save # keep lint happy
|
|
428
|
+
retval = super_save(*args, **kwargs)
|
|
429
|
+
|
|
430
|
+
pins = list(
|
|
431
|
+
ColonelPin.objects.select_for_update().filter(
|
|
432
|
+
colonel=self.colonel,
|
|
433
|
+
no__in=INTERFACES_PINS_MAP[self.no],
|
|
434
|
+
)
|
|
435
|
+
)
|
|
436
|
+
|
|
437
|
+
if self.type: # claim the pins
|
|
438
|
+
for pin in pins:
|
|
439
|
+
# If already occupied by another object – abort.
|
|
440
|
+
if pin.occupied_by and pin.occupied_by != self:
|
|
441
|
+
raise ValidationError(
|
|
442
|
+
f"Interface cannot claim {pin}. Currently "
|
|
443
|
+
f"occupied by {pin.occupied_by}.",
|
|
444
|
+
)
|
|
445
|
+
pin.occupied_by = self
|
|
446
|
+
ColonelPin.objects.bulk_update(
|
|
447
|
+
pins, ["occupied_by_content_type", "occupied_by_id"]
|
|
448
|
+
)
|
|
449
|
+
else: # release them if *we* were occupying
|
|
450
|
+
for pin in pins:
|
|
451
|
+
if pin.occupied_by == self:
|
|
452
|
+
pin.occupied_by = None
|
|
453
|
+
ColonelPin.objects.bulk_update(
|
|
454
|
+
pins, ["occupied_by_content_type", "occupied_by_id"]
|
|
455
|
+
)
|
|
456
|
+
|
|
457
|
+
return retval
|
|
417
458
|
|
|
418
459
|
def broadcast_reset(self):
|
|
419
460
|
from .gateways import FleetGatewayHandler
|
|
@@ -472,6 +513,11 @@ def post_interface_save(sender, instance, created, *args, **kwargs):
|
|
|
472
513
|
address=addr,
|
|
473
514
|
)
|
|
474
515
|
elif instance.type == 'dali':
|
|
516
|
+
InterfaceAddress.objects.filter(
|
|
517
|
+
interface=instance
|
|
518
|
+
).exclude(
|
|
519
|
+
address_type__in=('dali-gear', 'dali-group', 'dali-device')
|
|
520
|
+
).delete()
|
|
475
521
|
for addr in range(64):
|
|
476
522
|
InterfaceAddress.objects.get_or_create(
|
|
477
523
|
interface=instance, address_type='dali-gear',
|
|
@@ -485,17 +531,33 @@ def post_interface_save(sender, instance, created, *args, **kwargs):
|
|
|
485
531
|
else:
|
|
486
532
|
InterfaceAddress.objects.filter(interface=instance).delete()
|
|
487
533
|
|
|
534
|
+
try:
|
|
535
|
+
instance.colonel.update_config()
|
|
536
|
+
except Exception:
|
|
537
|
+
# Fail silently – configuration push should not prevent saving.
|
|
538
|
+
pass
|
|
539
|
+
|
|
488
540
|
|
|
489
541
|
|
|
490
542
|
@receiver(post_delete, sender=Interface)
|
|
491
543
|
def post_interface_delete(sender, instance, *args, **kwargs):
|
|
544
|
+
"""Release GPIO pins that were occupied by the removed Interface."""
|
|
492
545
|
with transaction.atomic():
|
|
493
546
|
ct = ContentType.objects.get_for_model(instance)
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
547
|
+
pins = list(
|
|
548
|
+
ColonelPin.objects.select_for_update().filter(
|
|
549
|
+
occupied_by_content_type=ct,
|
|
550
|
+
occupied_by_id=instance.id,
|
|
551
|
+
)
|
|
552
|
+
)
|
|
553
|
+
|
|
554
|
+
for pin in pins:
|
|
555
|
+
pin.occupied_by = None
|
|
556
|
+
|
|
557
|
+
if pins:
|
|
558
|
+
ColonelPin.objects.bulk_update(
|
|
559
|
+
pins, ["occupied_by_content_type", "occupied_by_id"]
|
|
560
|
+
)
|
|
499
561
|
|
|
500
562
|
|
|
501
563
|
class CustomDaliDevice(models.Model):
|
simo/fleet/serializers.py
CHANGED
|
@@ -46,7 +46,7 @@ class ColonelSerializer(serializers.ModelSerializer):
|
|
|
46
46
|
pins = serializers.SerializerMethodField()
|
|
47
47
|
interfaces = serializers.SerializerMethodField()
|
|
48
48
|
newer_firmware_available = serializers.SerializerMethodField()
|
|
49
|
-
last_seen = TimestampField()
|
|
49
|
+
last_seen = TimestampField(read_only=True)
|
|
50
50
|
is_empty = serializers.SerializerMethodField()
|
|
51
51
|
|
|
52
52
|
class Meta:
|
|
@@ -91,7 +91,7 @@ class ColonelSerializer(serializers.ModelSerializer):
|
|
|
91
91
|
class CustomDaliDeviceSerializer(serializers.ModelSerializer):
|
|
92
92
|
is_empty = serializers.SerializerMethodField()
|
|
93
93
|
is_alive = serializers.SerializerMethodField()
|
|
94
|
-
last_seen = TimestampField()
|
|
94
|
+
last_seen = TimestampField(read_only=True)
|
|
95
95
|
|
|
96
96
|
class Meta:
|
|
97
97
|
model = CustomDaliDevice
|
simo/fleet/utils.py
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
from django.db import transaction, models
|
|
2
|
+
from django.core.exceptions import ValidationError
|
|
3
|
+
from django.contrib.contenttypes.models import ContentType
|
|
1
4
|
from simo.core.utils.cache import get_cached_data
|
|
2
5
|
from simo.core.middleware import get_current_instance
|
|
3
6
|
|
|
7
|
+
|
|
4
8
|
GPIO_PIN_DEFAULTS = {
|
|
5
9
|
'output': True, 'input': True, 'default_pull': 'FLOATING',
|
|
6
10
|
'native': True, 'adc': False,
|
|
@@ -71,6 +75,10 @@ for no, data in BASE_ESP32_GPIO_PINS.items():
|
|
|
71
75
|
GPIO_PINS['game-changer'][no] = GPIO_PIN_DEFAULTS.copy()
|
|
72
76
|
GPIO_PINS['game-changer'][no].update(data)
|
|
73
77
|
|
|
78
|
+
if no in (13, 23, 32, 33):
|
|
79
|
+
GPIO_PINS['game-changer-mini'][no] = GPIO_PIN_DEFAULTS.copy()
|
|
80
|
+
GPIO_PINS['game-changer-mini'][no].update(data)
|
|
81
|
+
|
|
74
82
|
|
|
75
83
|
for no in range(101, 126):
|
|
76
84
|
GPIO_PINS['ample-wall'][no] = {
|
|
@@ -78,6 +86,9 @@ for no in range(101, 126):
|
|
|
78
86
|
'native': False, 'adc': False,
|
|
79
87
|
'capacitive': False, 'note': ''
|
|
80
88
|
}
|
|
89
|
+
if no in (101, 102):
|
|
90
|
+
GPIO_PINS['ample-wall'][no]['interface'] = no - 100
|
|
91
|
+
|
|
81
92
|
for no in range(126, 133):
|
|
82
93
|
GPIO_PINS['ample-wall'][no] = {
|
|
83
94
|
'output': True, 'input': True, 'default_pull': 'HIGH',
|
|
@@ -92,6 +103,8 @@ for no in range(101, 139):
|
|
|
92
103
|
'native': False, 'adc': False,
|
|
93
104
|
'capacitive': False, 'note': ''
|
|
94
105
|
}
|
|
106
|
+
if no in (101, 102):
|
|
107
|
+
GPIO_PINS['game-changer'][no]['interface'] = no - 100
|
|
95
108
|
|
|
96
109
|
for no in range(101, 105):
|
|
97
110
|
GPIO_PINS['game-changer-mini'][no] = {
|
|
@@ -99,6 +112,8 @@ for no in range(101, 105):
|
|
|
99
112
|
'native': False, 'adc': False,
|
|
100
113
|
'capacitive': False, 'note': ''
|
|
101
114
|
}
|
|
115
|
+
if no in (101, 102):
|
|
116
|
+
GPIO_PINS['game-changer-mini'][no]['interface'] = no - 100
|
|
102
117
|
|
|
103
118
|
|
|
104
119
|
#4-relays
|
|
@@ -134,6 +149,117 @@ INTERFACES_PINS_MAP = {
|
|
|
134
149
|
}
|
|
135
150
|
|
|
136
151
|
|
|
152
|
+
def _get_component_interface_addresses(component):
|
|
153
|
+
"""Return list[(interface_id, address_type, address)] component needs.
|
|
154
|
+
|
|
155
|
+
Supports I²C and DALI controllers. Extend when new bus types land.
|
|
156
|
+
"""
|
|
157
|
+
from .models import Interface # local import to avoid circular deps
|
|
158
|
+
|
|
159
|
+
cfg = component.config or {}
|
|
160
|
+
desired = []
|
|
161
|
+
|
|
162
|
+
# I²C ----------------------------------------------------------------
|
|
163
|
+
if 'i2c_interface' in cfg and 'i2c_address' in cfg:
|
|
164
|
+
desired.append((cfg['i2c_interface'], 'i2c', cfg['i2c_address']))
|
|
165
|
+
|
|
166
|
+
# DALI ---------------------------------------------------------------
|
|
167
|
+
interface_no = None
|
|
168
|
+
if 'dali_interface' in cfg:
|
|
169
|
+
interface_no = cfg['dali_interface']
|
|
170
|
+
elif 'interface' in cfg:
|
|
171
|
+
interface_no = cfg['interface']
|
|
172
|
+
|
|
173
|
+
if interface_no is not None and 'colonel' in cfg and 'da' in cfg:
|
|
174
|
+
interface_obj = Interface.objects.filter(
|
|
175
|
+
colonel_id=cfg['colonel'], no=interface_no
|
|
176
|
+
).first()
|
|
177
|
+
if interface_obj:
|
|
178
|
+
uid = component.controller_uid or ''
|
|
179
|
+
if uid.endswith('DALIGearGroup'):
|
|
180
|
+
addr_type = 'dali-group'
|
|
181
|
+
elif uid.endswith(('DALILamp', 'DALIRelay')):
|
|
182
|
+
addr_type = 'dali-gear'
|
|
183
|
+
else:
|
|
184
|
+
addr_type = 'dali-device'
|
|
185
|
+
desired.append((interface_obj.id, addr_type, cfg['da']))
|
|
186
|
+
|
|
187
|
+
return desired
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def _sync_interface_address_occupancy(component):
|
|
191
|
+
"""Synchronise InterfaceAddress rows for *component* (claim/release)."""
|
|
192
|
+
from .models import InterfaceAddress # local import, avoids circulars
|
|
193
|
+
|
|
194
|
+
desired = set(_get_component_interface_addresses(component))
|
|
195
|
+
|
|
196
|
+
# Short-circuit if nothing desired and nothing currently occupied.
|
|
197
|
+
if not desired and not InterfaceAddress.objects.filter(
|
|
198
|
+
occupied_by_id=component.id,
|
|
199
|
+
occupied_by_content_type=ContentType.objects.get_for_model(component),
|
|
200
|
+
).exists():
|
|
201
|
+
return
|
|
202
|
+
|
|
203
|
+
with transaction.atomic():
|
|
204
|
+
ct = ContentType.objects.get_for_model(component)
|
|
205
|
+
|
|
206
|
+
iface_ids = [d[0] for d in desired]
|
|
207
|
+
lock_qs = InterfaceAddress.objects.select_for_update().filter(
|
|
208
|
+
models.Q(occupied_by_content_type=ct, occupied_by_id=component.id)
|
|
209
|
+
| models.Q(interface_id__in=iface_ids)
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
addr_map = {(
|
|
213
|
+
ia.interface_id, ia.address_type, ia.address): ia for ia in lock_qs}
|
|
214
|
+
|
|
215
|
+
# Release obsolete ------------------------------------------------
|
|
216
|
+
for ia in lock_qs:
|
|
217
|
+
key = (ia.interface_id, ia.address_type, ia.address)
|
|
218
|
+
if ia.occupied_by_id == component.id and key not in desired:
|
|
219
|
+
ia.occupied_by = None
|
|
220
|
+
|
|
221
|
+
# Claim desired ---------------------------------------------------
|
|
222
|
+
for iface_id, addr_type, addr_val in desired:
|
|
223
|
+
key = (iface_id, addr_type, addr_val)
|
|
224
|
+
ia = addr_map.get(key)
|
|
225
|
+
if not ia:
|
|
226
|
+
ia = InterfaceAddress.objects.select_for_update().create(
|
|
227
|
+
interface_id=iface_id, address_type=addr_type,
|
|
228
|
+
address=addr_val,
|
|
229
|
+
)
|
|
230
|
+
addr_map[key] = ia
|
|
231
|
+
if ia.occupied_by and ia.occupied_by != component:
|
|
232
|
+
raise ValidationError(
|
|
233
|
+
f"Interface address {ia} already occupied by "
|
|
234
|
+
f"{ia.occupied_by}."
|
|
235
|
+
)
|
|
236
|
+
ia.occupied_by = component
|
|
237
|
+
|
|
238
|
+
# Bulk save all modified objects
|
|
239
|
+
InterfaceAddress.objects.bulk_update(
|
|
240
|
+
addr_map.values(),
|
|
241
|
+
["occupied_by_content_type", "occupied_by_id"],
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def _release_interface_addresses(component):
|
|
246
|
+
"""Clear all InterfaceAddress rows owned by component."""
|
|
247
|
+
from .models import InterfaceAddress # delayed import
|
|
248
|
+
|
|
249
|
+
ct = ContentType.objects.get_for_model(component)
|
|
250
|
+
with transaction.atomic():
|
|
251
|
+
addresses = InterfaceAddress.objects.select_for_update().filter(
|
|
252
|
+
occupied_by_content_type=ct, occupied_by_id=component.id,
|
|
253
|
+
)
|
|
254
|
+
if not addresses:
|
|
255
|
+
return
|
|
256
|
+
for ia in addresses:
|
|
257
|
+
ia.occupied_by = None
|
|
258
|
+
InterfaceAddress.objects.bulk_update(
|
|
259
|
+
addresses, ["occupied_by_content_type", "occupied_by_id"]
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
|
|
137
263
|
def get_all_control_input_choices():
|
|
138
264
|
'''
|
|
139
265
|
This is called multiple times by component form,
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
simo/multimedia/migrations/__pycache__/0005_remove_sound_slug_sound_date_uploaded.cpython-312.pyc
CHANGED
|
Binary file
|
simo/multimedia/migrations/__pycache__/0006_remove_sound_length_sound_duration.cpython-312.pyc
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|