simo 2.10.6__py3-none-any.whl → 2.10.9__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 +16 -10
- simo/fleet/controllers.py +98 -14
- simo/fleet/forms.py +70 -110
- simo/fleet/gateways.py +34 -4
- simo/fleet/migrations/0050_customdalidevice_uid.py +19 -0
- simo/fleet/migrations/0051_customdalidevice_components.py +19 -0
- simo/fleet/migrations/0052_colonelpin_interface.py +18 -0
- simo/fleet/migrations/0053_auto_20250507_0713.py +24 -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__/__init__.cpython-312.pyc +0 -0
- simo/fleet/models.py +124 -34
- simo/fleet/serializers.py +40 -4
- 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/controllers.py +2 -2
- 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.6.dist-info → simo-2.10.9.dist-info}/METADATA +1 -1
- {simo-2.10.6.dist-info → simo-2.10.9.dist-info}/RECORD +389 -337
- {simo-2.10.6.dist-info → simo-2.10.9.dist-info}/WHEEL +1 -1
- {simo-2.10.6.dist-info → simo-2.10.9.dist-info}/entry_points.txt +0 -0
- {simo-2.10.6.dist-info → simo-2.10.9.dist-info}/licenses/LICENSE.md +0 -0
- {simo-2.10.6.dist-info → simo-2.10.9.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Generated by Django 4.2.10 on 2025-05-07 07:13
|
|
2
|
+
|
|
3
|
+
from django.db import migrations
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def forwards_func(apps, schema_editor):
|
|
7
|
+
ColonelPin = apps.get_model("fleet", "ColonelPin")
|
|
8
|
+
ColonelPin.objects.filter(no=101).update(interface=1)
|
|
9
|
+
ColonelPin.objects.filter(no=102).update(interface=2)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def reverse_func(apps, schema_editor):
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Migration(migrations.Migration):
|
|
17
|
+
|
|
18
|
+
dependencies = [
|
|
19
|
+
('fleet', '0052_colonelpin_interface'),
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
operations = [
|
|
23
|
+
migrations.RunPython(forwards_func, reverse_func, elidable=True),
|
|
24
|
+
]
|
|
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/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
|
simo/fleet/models.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import requests
|
|
2
2
|
import time
|
|
3
3
|
import random
|
|
4
|
+
import datetime
|
|
4
5
|
from actstream import action
|
|
5
6
|
from django.core.exceptions import ValidationError
|
|
6
7
|
from django.db import transaction
|
|
@@ -9,12 +10,17 @@ from django.db.models.signals import post_save, pre_delete, post_delete
|
|
|
9
10
|
from django.dispatch import receiver
|
|
10
11
|
from django.contrib.contenttypes.models import ContentType
|
|
11
12
|
from django.contrib.contenttypes.fields import GenericForeignKey
|
|
13
|
+
from django.utils import timezone
|
|
12
14
|
from dirtyfields import DirtyFieldsMixin
|
|
13
15
|
from simo.core.models import Instance, Gateway, Component
|
|
14
16
|
from simo.core.utils.helpers import get_random_string
|
|
15
17
|
from simo.core.events import GatewayObjectCommand
|
|
16
18
|
from .managers import ColonelsManager, ColonelPinsManager, InterfacesManager
|
|
17
|
-
|
|
19
|
+
# Now imported from utils
|
|
20
|
+
from .utils import GPIO_PINS, INTERFACES_PINS_MAP, \
|
|
21
|
+
_sync_interface_address_occupancy, _release_interface_addresses
|
|
22
|
+
|
|
23
|
+
# -------------------------------------------------------------------------
|
|
18
24
|
|
|
19
25
|
|
|
20
26
|
|
|
@@ -217,6 +223,7 @@ class ColonelPin(models.Model):
|
|
|
217
223
|
capacitive = models.BooleanField(default=False, db_index=True)
|
|
218
224
|
adc = models.BooleanField(default=False)
|
|
219
225
|
native = models.BooleanField(default=True, db_index=True)
|
|
226
|
+
interface = models.PositiveIntegerField(null=True, blank=True)
|
|
220
227
|
default_pull = models.CharField(
|
|
221
228
|
max_length=50, db_index=True, null=True, blank=True,
|
|
222
229
|
choices=(('LOW', "LOW"), ("HIGH", "HIGH"))
|
|
@@ -244,6 +251,12 @@ class ColonelPin(models.Model):
|
|
|
244
251
|
if not self.label:
|
|
245
252
|
# Might be created via migration...
|
|
246
253
|
self.save()
|
|
254
|
+
if self.interface:
|
|
255
|
+
interface = Interface.objects.filter(
|
|
256
|
+
colonel=self.colonel, no=self.interface
|
|
257
|
+
).first()
|
|
258
|
+
if interface:
|
|
259
|
+
return f"{self.label} - {interface.get_type_display()}"
|
|
247
260
|
return self.label
|
|
248
261
|
|
|
249
262
|
def save(self, *args, **kwargs):
|
|
@@ -266,7 +279,8 @@ def after_colonel_save(sender, instance, created, *args, **kwargs):
|
|
|
266
279
|
defaults={
|
|
267
280
|
'input': data.get('input'), 'output': data.get('output'),
|
|
268
281
|
'capacitive': data.get('capacitive'), 'adc': data.get('adc'),
|
|
269
|
-
'native': data.get('native'), 'note': data.get('note')
|
|
282
|
+
'native': data.get('native'), 'note': data.get('note'),
|
|
283
|
+
'interface': data.get('interface')
|
|
270
284
|
}
|
|
271
285
|
)
|
|
272
286
|
fleet_gateway, new = Gateway.objects.get_or_create(
|
|
@@ -274,15 +288,6 @@ def after_colonel_save(sender, instance, created, *args, **kwargs):
|
|
|
274
288
|
)
|
|
275
289
|
if fleet_gateway.status != 'running':
|
|
276
290
|
fleet_gateway.start()
|
|
277
|
-
# create i2c and dali interfaces automatically for game-changer boards
|
|
278
|
-
if instance.type == 'game-changer':
|
|
279
|
-
# occupy ports immediately
|
|
280
|
-
Interface.objects.create(colonel=instance, no=1, type='i2c')
|
|
281
|
-
Interface.objects.create(colonel=instance, no=2, type='dali')
|
|
282
|
-
elif instance.type == 'game-changer-mini':
|
|
283
|
-
# only create interfaces, but do not occupy ports
|
|
284
|
-
Interface.objects.create(colonel=instance, no=1)
|
|
285
|
-
Interface.objects.create(colonel=instance, no=2)
|
|
286
291
|
|
|
287
292
|
if 'socket_connected' in instance.get_dirty_fields():
|
|
288
293
|
if instance.socket_connected:
|
|
@@ -306,15 +311,26 @@ def post_component_save(sender, instance, created, *args, **kwargs):
|
|
|
306
311
|
if not colonel:
|
|
307
312
|
return
|
|
308
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
|
+
|
|
309
323
|
from .controllers import (
|
|
310
324
|
TTLock, DALILamp, DALIGearGroup, DALIRelay, DALIOccupancySensor,
|
|
311
325
|
DALILightSensor, DALIButton,
|
|
312
|
-
AirQualitySensor, TempHumSensor, AmbientLightSensor,
|
|
326
|
+
AirQualitySensor, TempHumSensor, AmbientLightSensor,
|
|
327
|
+
RoomPresenceSensor, RoomZonePresenceSensor
|
|
313
328
|
)
|
|
314
329
|
if instance.controller and instance.controller_cls in (
|
|
315
330
|
TTLock, DALILamp, DALIGearGroup, DALIRelay, DALIOccupancySensor,
|
|
316
331
|
DALILightSensor, DALIButton,
|
|
317
|
-
AirQualitySensor, TempHumSensor, AmbientLightSensor,
|
|
332
|
+
AirQualitySensor, TempHumSensor, AmbientLightSensor,
|
|
333
|
+
RoomPresenceSensor, RoomZonePresenceSensor
|
|
318
334
|
):
|
|
319
335
|
return
|
|
320
336
|
colonel.rebuild_occupied_pins()
|
|
@@ -326,6 +342,9 @@ def post_component_save(sender, instance, created, *args, **kwargs):
|
|
|
326
342
|
@receiver(pre_delete, sender=Component)
|
|
327
343
|
def post_component_delete(sender, instance, *args, **kwargs):
|
|
328
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)
|
|
329
348
|
return
|
|
330
349
|
|
|
331
350
|
from .controllers import DALIGearGroup
|
|
@@ -357,6 +376,9 @@ def post_component_delete(sender, instance, *args, **kwargs):
|
|
|
357
376
|
|
|
358
377
|
transaction.on_commit(update_colonel)
|
|
359
378
|
|
|
379
|
+
# Finally release any InterfaceAddress rows occupied by this component
|
|
380
|
+
_release_interface_addresses(instance)
|
|
381
|
+
|
|
360
382
|
|
|
361
383
|
class Interface(models.Model):
|
|
362
384
|
colonel = models.ForeignKey(
|
|
@@ -391,27 +413,48 @@ class Interface(models.Model):
|
|
|
391
413
|
def save(self, *args, **kwargs):
|
|
392
414
|
if not self.pin_a:
|
|
393
415
|
self.pin_a = ColonelPin.objects.get(
|
|
394
|
-
colonel=self.colonel,
|
|
416
|
+
colonel=self.colonel,
|
|
417
|
+
no=INTERFACES_PINS_MAP[self.no][0],
|
|
395
418
|
)
|
|
396
419
|
if not self.pin_b:
|
|
397
420
|
self.pin_b = ColonelPin.objects.get(
|
|
398
|
-
colonel=self.colonel,
|
|
421
|
+
colonel=self.colonel,
|
|
422
|
+
no=INTERFACES_PINS_MAP[self.no][1],
|
|
399
423
|
)
|
|
400
|
-
if self.type:
|
|
401
|
-
for pin_no in INTERFACES_PINS_MAP[self.no]:
|
|
402
|
-
cpin = ColonelPin.objects.get(colonel=self.colonel, no=pin_no)
|
|
403
|
-
if cpin.occupied_by and cpin.occupied_by != self:
|
|
404
|
-
raise ValidationError(
|
|
405
|
-
f"Interface can not be created, because "
|
|
406
|
-
f"{cpin} is already occupied by {cpin.occupied_by}."
|
|
407
|
-
)
|
|
408
|
-
self.pin_a.occupied_by = self
|
|
409
|
-
self.pin_b.occupied_by = self
|
|
410
|
-
else:
|
|
411
|
-
self.pin_a.occupied_by = None
|
|
412
|
-
self.pin_b.occupied_by = None
|
|
413
424
|
|
|
414
|
-
|
|
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
|
|
415
458
|
|
|
416
459
|
def broadcast_reset(self):
|
|
417
460
|
from .gateways import FleetGatewayHandler
|
|
@@ -470,6 +513,11 @@ def post_interface_save(sender, instance, created, *args, **kwargs):
|
|
|
470
513
|
address=addr,
|
|
471
514
|
)
|
|
472
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()
|
|
473
521
|
for addr in range(64):
|
|
474
522
|
InterfaceAddress.objects.get_or_create(
|
|
475
523
|
interface=instance, address_type='dali-gear',
|
|
@@ -483,17 +531,33 @@ def post_interface_save(sender, instance, created, *args, **kwargs):
|
|
|
483
531
|
else:
|
|
484
532
|
InterfaceAddress.objects.filter(interface=instance).delete()
|
|
485
533
|
|
|
534
|
+
try:
|
|
535
|
+
instance.colonel.update_config()
|
|
536
|
+
except Exception:
|
|
537
|
+
# Fail silently – configuration push should not prevent saving.
|
|
538
|
+
pass
|
|
539
|
+
|
|
486
540
|
|
|
487
541
|
|
|
488
542
|
@receiver(post_delete, sender=Interface)
|
|
489
543
|
def post_interface_delete(sender, instance, *args, **kwargs):
|
|
544
|
+
"""Release GPIO pins that were occupied by the removed Interface."""
|
|
490
545
|
with transaction.atomic():
|
|
491
546
|
ct = ContentType.objects.get_for_model(instance)
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
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
|
+
)
|
|
497
561
|
|
|
498
562
|
|
|
499
563
|
class CustomDaliDevice(models.Model):
|
|
@@ -502,6 +566,7 @@ class CustomDaliDevice(models.Model):
|
|
|
502
566
|
not compatible with anything else of DALI!
|
|
503
567
|
'''
|
|
504
568
|
instance = models.ForeignKey(Instance, on_delete=models.CASCADE)
|
|
569
|
+
uid = models.CharField(max_length=100, db_index=True)
|
|
505
570
|
random_address = models.PositiveIntegerField(db_index=True, editable=False)
|
|
506
571
|
name = models.CharField(
|
|
507
572
|
max_length=200, help_text="User given name on initial pairing"
|
|
@@ -512,6 +577,7 @@ class CustomDaliDevice(models.Model):
|
|
|
512
577
|
help_text="Colonel interface on which it operates."
|
|
513
578
|
)
|
|
514
579
|
last_seen = models.DateTimeField(null=True, editable=False)
|
|
580
|
+
components = models.ManyToManyField(Component)
|
|
515
581
|
|
|
516
582
|
class Meta:
|
|
517
583
|
unique_together = 'instance', 'random_address'
|
|
@@ -538,3 +604,27 @@ class CustomDaliDevice(models.Model):
|
|
|
538
604
|
msg=frame.pack.hex()
|
|
539
605
|
).publish()
|
|
540
606
|
|
|
607
|
+
@property
|
|
608
|
+
def is_alive(self):
|
|
609
|
+
if not self.last_seen:
|
|
610
|
+
return False
|
|
611
|
+
return self.last_seen + datetime.timedelta(seconds=60) > timezone.now()
|
|
612
|
+
|
|
613
|
+
|
|
614
|
+
@receiver(post_save, sender=Component)
|
|
615
|
+
def attatch_components_to_dali_device(sender, instance, created, *args, **kwargs):
|
|
616
|
+
if not instance.controller_uid.startswith('simo.fleet'):
|
|
617
|
+
return
|
|
618
|
+
if 'config' not in instance.get_dirty_fields():
|
|
619
|
+
return
|
|
620
|
+
dali_device = CustomDaliDevice.objects.filter(
|
|
621
|
+
id=instance.config.get('dali_device', 0)
|
|
622
|
+
).first()
|
|
623
|
+
if not dali_device:
|
|
624
|
+
return
|
|
625
|
+
dali_device.components.add(instance)
|
|
626
|
+
|
|
627
|
+
|
|
628
|
+
@receiver(pre_delete, sender=CustomDaliDevice)
|
|
629
|
+
def delete_dali_device_components(sender, instance, *args, **kwargs):
|
|
630
|
+
instance.components.all().delete()
|
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:
|
|
@@ -89,12 +89,48 @@ class ColonelSerializer(serializers.ModelSerializer):
|
|
|
89
89
|
|
|
90
90
|
|
|
91
91
|
class CustomDaliDeviceSerializer(serializers.ModelSerializer):
|
|
92
|
+
is_empty = serializers.SerializerMethodField()
|
|
93
|
+
is_alive = serializers.SerializerMethodField()
|
|
94
|
+
last_seen = TimestampField(read_only=True)
|
|
92
95
|
|
|
93
96
|
class Meta:
|
|
94
97
|
model = CustomDaliDevice
|
|
95
|
-
fields =
|
|
96
|
-
|
|
98
|
+
fields = (
|
|
99
|
+
'id', 'uid', 'random_address', 'name', 'is_empty',
|
|
100
|
+
'is_alive', 'last_seen'
|
|
101
|
+
)
|
|
102
|
+
read_only_fields = (
|
|
103
|
+
'random_address', 'is_empty', 'is_alive', 'last_seen'
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
def validate(self, data):
|
|
107
|
+
instance = self.context.get('instance')
|
|
108
|
+
uid = data.get('uid')
|
|
109
|
+
if instance and uid:
|
|
110
|
+
if CustomDaliDevice.objects.filter(
|
|
111
|
+
uid=uid, instance=instance
|
|
112
|
+
).exists():
|
|
113
|
+
raise serializers.ValidationError(
|
|
114
|
+
f"A device with uid '{uid}' already exists for this instance."
|
|
115
|
+
)
|
|
116
|
+
return data
|
|
117
|
+
|
|
118
|
+
def validate_uid(self, value):
|
|
119
|
+
"""
|
|
120
|
+
Prevent changing the uid on update.
|
|
121
|
+
"""
|
|
122
|
+
# self.instance will be None for creation, but set for updates.
|
|
123
|
+
if self.instance and self.instance.uid != value:
|
|
124
|
+
raise serializers.ValidationError("Changing uid is not allowed.")
|
|
125
|
+
return value
|
|
97
126
|
|
|
98
127
|
def create(self, validated_data):
|
|
99
128
|
validated_data['instance'] = self.context['instance']
|
|
100
|
-
return super().create(validated_data)
|
|
129
|
+
return super().create(validated_data)
|
|
130
|
+
|
|
131
|
+
def get_is_empty(self, obj):
|
|
132
|
+
return not bool(obj.components.all().count())
|
|
133
|
+
|
|
134
|
+
def get_is_alive(self, obj):
|
|
135
|
+
return obj.is_alive
|
|
136
|
+
|