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
simo/fleet/forms.py
CHANGED
|
@@ -460,65 +460,86 @@ class ColonelDHTSensorConfigForm(ColonelComponentForm):
|
|
|
460
460
|
return super().save(commit=commit)
|
|
461
461
|
|
|
462
462
|
|
|
463
|
-
class
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
463
|
+
class I2CDevice(ColonelComponentForm):
|
|
464
|
+
interface_port = Select2ModelChoiceField(
|
|
465
|
+
label="Interface",
|
|
466
|
+
queryset=ColonelPin.objects.filter(interface__isnull=False),
|
|
467
|
+
url='autocomplete-colonel-pins',
|
|
467
468
|
forward=[
|
|
468
469
|
forward.Self(),
|
|
469
470
|
forward.Field('colonel'),
|
|
470
|
-
forward.Const(
|
|
471
|
-
|
|
472
|
-
)
|
|
473
|
-
]
|
|
474
|
-
)
|
|
475
|
-
i2c_address = forms.TypedChoiceField(
|
|
476
|
-
coerce=int, initial=118,
|
|
477
|
-
choices=((118, "0x76"), (119, "0x77")),
|
|
478
|
-
)
|
|
479
|
-
read_frequency_s = forms.IntegerField(
|
|
480
|
-
initial=60, min_value=1, max_value=60*60*24,
|
|
481
|
-
help_text='read and report climate value every s. '
|
|
482
|
-
'Can not be less than 1s.'
|
|
483
|
-
|
|
471
|
+
forward.Const({'interface__isnull': False}, 'filters'),
|
|
472
|
+
],
|
|
484
473
|
)
|
|
485
474
|
|
|
486
475
|
def clean(self):
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
476
|
+
cleaned_data = super().clean()
|
|
477
|
+
colonel = cleaned_data.get('colonel')
|
|
478
|
+
port_choice = cleaned_data.get('interface_port')
|
|
479
|
+
if not colonel or not port_choice:
|
|
480
|
+
return cleaned_data
|
|
481
|
+
|
|
482
|
+
# Create or fetch the I²C interface
|
|
483
|
+
interface, created = Interface.objects.get_or_create(
|
|
484
|
+
colonel=colonel,
|
|
485
|
+
no=port_choice.interface,
|
|
486
|
+
defaults={'type': 'i2c'},
|
|
487
|
+
)
|
|
488
|
+
|
|
489
|
+
# If it already existed as something else, ensure it's free
|
|
490
|
+
if interface.type != 'i2c':
|
|
491
|
+
occupied = interface.addresses.filter(occupied_by__isnull=False).first()
|
|
492
|
+
if occupied:
|
|
493
|
+
self.add_error(
|
|
494
|
+
'interface_port',
|
|
495
|
+
f"Port already occupied by {occupied.occupied_by}"
|
|
496
|
+
)
|
|
497
|
+
return cleaned_data
|
|
498
|
+
interface.type = 'i2c'
|
|
499
|
+
interface.save()
|
|
500
|
+
|
|
501
|
+
# Check for address collisions on that interface
|
|
502
|
+
other = Component.objects.filter(
|
|
503
|
+
config__colonel=colonel.id,
|
|
504
|
+
config__interface=interface.id,
|
|
505
|
+
config__i2c_address=cleaned_data['i2c_address'],
|
|
506
|
+
).exclude(id=self.instance.id).first()
|
|
507
|
+
if other:
|
|
490
508
|
self.add_error(
|
|
491
|
-
'
|
|
492
|
-
f"
|
|
493
|
-
f"however we need an interface from {self.cleaned_data['colonel']}."
|
|
509
|
+
'i2c_address',
|
|
510
|
+
f"Address already occupied by {other}"
|
|
494
511
|
)
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
).exclude(id=self.instance.id).first()
|
|
500
|
-
if other_comp:
|
|
501
|
-
self.add_error('i2c_address', f'Already occupied by {other_comp}!')
|
|
502
|
-
return self.cleaned_data
|
|
512
|
+
|
|
513
|
+
# stash for save()
|
|
514
|
+
cleaned_data['i2c_interface'] = interface.id
|
|
515
|
+
return cleaned_data
|
|
503
516
|
|
|
504
517
|
def save(self, commit=True):
|
|
505
|
-
|
|
506
|
-
self.instance.config['i2c_interface'] = self.cleaned_data['interface'].no
|
|
518
|
+
self.instance.config['i2c_interface'] = self.cleaned_data['i2c_interface']
|
|
507
519
|
return super().save(commit=commit)
|
|
508
520
|
|
|
509
521
|
|
|
510
|
-
class
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
{'type': 'i2c'}, 'filters'
|
|
519
|
-
)
|
|
520
|
-
]
|
|
522
|
+
class BME680SensorConfigForm(I2CDevice):
|
|
523
|
+
i2c_address = forms.TypedChoiceField(
|
|
524
|
+
coerce=int,
|
|
525
|
+
initial=119, # match “0x77 – default”
|
|
526
|
+
choices=(
|
|
527
|
+
(119, "0x77 – default"),
|
|
528
|
+
(118, "0x76 – soldered"),
|
|
529
|
+
),
|
|
521
530
|
)
|
|
531
|
+
read_frequency_s = forms.IntegerField(
|
|
532
|
+
initial=60,
|
|
533
|
+
min_value=1,
|
|
534
|
+
max_value=60 * 60 * 24,
|
|
535
|
+
help_text=(
|
|
536
|
+
"Read and report climate value every second. "
|
|
537
|
+
"Cannot be less than 1 second."
|
|
538
|
+
),
|
|
539
|
+
)
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
class MCP9808SensorConfigForm(I2CDevice):
|
|
522
543
|
i2c_address = forms.TypedChoiceField(
|
|
523
544
|
coerce=int, initial=24,
|
|
524
545
|
choices=(
|
|
@@ -535,43 +556,8 @@ class MCP9808SensorConfigForm(ColonelComponentForm):
|
|
|
535
556
|
|
|
536
557
|
)
|
|
537
558
|
|
|
538
|
-
def clean(self):
|
|
539
|
-
if not self.cleaned_data.get('colonel'):
|
|
540
|
-
return self.cleaned_data
|
|
541
|
-
if self.cleaned_data['interface'].colonel != self.cleaned_data['colonel']:
|
|
542
|
-
self.add_error(
|
|
543
|
-
'interface',
|
|
544
|
-
f"This interface is on {self.cleaned_data['interface'].colonel}, "
|
|
545
|
-
f"however we need an interface from {self.cleaned_data['colonel']}."
|
|
546
|
-
)
|
|
547
|
-
|
|
548
|
-
other_comp = Component.objects.filter(
|
|
549
|
-
config__colonel=self.cleaned_data['colonel'].id,
|
|
550
|
-
config__interface=self.cleaned_data['interface'].id,
|
|
551
|
-
config__i2c_address=self.cleaned_data['i2c_address']
|
|
552
|
-
).exclude(id=self.instance.id).first()
|
|
553
|
-
if other_comp:
|
|
554
|
-
self.add_error('i2c_address', f'Already occupied by {other_comp}!')
|
|
555
|
-
return self.cleaned_data
|
|
556
|
-
|
|
557
|
-
def save(self, commit=True):
|
|
558
|
-
if 'interface' in self.cleaned_data:
|
|
559
|
-
self.instance.config['i2c_interface'] = self.cleaned_data['interface'].no
|
|
560
|
-
return super().save(commit=commit)
|
|
561
|
-
|
|
562
559
|
|
|
563
|
-
class ENS160SensorConfigForm(
|
|
564
|
-
interface = Select2ModelChoiceField(
|
|
565
|
-
queryset=Interface.objects.filter(type='i2c'),
|
|
566
|
-
url='autocomplete-interfaces',
|
|
567
|
-
forward=[
|
|
568
|
-
forward.Self(),
|
|
569
|
-
forward.Field('colonel'),
|
|
570
|
-
forward.Const(
|
|
571
|
-
{'type': 'i2c'}, 'filters'
|
|
572
|
-
)
|
|
573
|
-
]
|
|
574
|
-
)
|
|
560
|
+
class ENS160SensorConfigForm(I2CDevice):
|
|
575
561
|
i2c_address = forms.TypedChoiceField(
|
|
576
562
|
coerce=int, initial=83,
|
|
577
563
|
choices=((82, "0x52"), (83, "0x53")),
|
|
@@ -583,30 +569,6 @@ class ENS160SensorConfigForm(ColonelComponentForm):
|
|
|
583
569
|
|
|
584
570
|
)
|
|
585
571
|
|
|
586
|
-
def clean(self):
|
|
587
|
-
if not self.cleaned_data.get('colonel'):
|
|
588
|
-
return self.cleaned_data
|
|
589
|
-
if self.cleaned_data['interface'].colonel != self.cleaned_data['colonel']:
|
|
590
|
-
self.add_error(
|
|
591
|
-
'interface',
|
|
592
|
-
f"This interface is on {self.cleaned_data['interface'].colonel}, "
|
|
593
|
-
f"however we need an interface from {self.cleaned_data['colonel']}."
|
|
594
|
-
)
|
|
595
|
-
other_comp = Component.objects.filter(
|
|
596
|
-
config__colonel=self.cleaned_data['colonel'].id,
|
|
597
|
-
config__interface=self.cleaned_data['interface'].id,
|
|
598
|
-
config__i2c_address=self.cleaned_data['i2c_address']
|
|
599
|
-
).exclude(id=self.instance.id).first()
|
|
600
|
-
if other_comp:
|
|
601
|
-
self.add_error('i2c_address', f'Already occupied by {other_comp}!')
|
|
602
|
-
return self.cleaned_data
|
|
603
|
-
|
|
604
|
-
def save(self, commit=True):
|
|
605
|
-
if 'interface' in self.cleaned_data:
|
|
606
|
-
self.instance.config['i2c_interface'] = \
|
|
607
|
-
self.cleaned_data['interface'].no
|
|
608
|
-
return super().save(commit=commit)
|
|
609
|
-
|
|
610
572
|
|
|
611
573
|
class ColonelTouchSensorConfigForm(ColonelComponentForm):
|
|
612
574
|
pin = Select2ModelChoiceField(
|
|
@@ -727,7 +689,75 @@ class ColonelSwitchConfigForm(ColonelComponentForm):
|
|
|
727
689
|
return obj
|
|
728
690
|
|
|
729
691
|
|
|
730
|
-
class
|
|
692
|
+
class PWMOutputBaseConfig(ColonelComponentForm):
|
|
693
|
+
|
|
694
|
+
def __init__(self, *args, **kwargs):
|
|
695
|
+
super().__init__(*args, **kwargs)
|
|
696
|
+
if 'value_units' in self.fields:
|
|
697
|
+
self.fields['value_units'].initial = self.controller.default_value_units
|
|
698
|
+
self.basic_fields.extend(
|
|
699
|
+
['value_units', 'turn_on_time', 'turn_off_time', 'skew']
|
|
700
|
+
)
|
|
701
|
+
if self.instance.pk and 'slaves' in self.fields:
|
|
702
|
+
self.fields['slaves'].initial = self.instance.slaves.all()
|
|
703
|
+
|
|
704
|
+
def clean_slaves(self):
|
|
705
|
+
if not self.cleaned_data['slaves'] or not self.instance:
|
|
706
|
+
return self.cleaned_data['slaves']
|
|
707
|
+
return validate_slaves(self.cleaned_data['slaves'], self.instance)
|
|
708
|
+
|
|
709
|
+
def clean(self):
|
|
710
|
+
super().clean()
|
|
711
|
+
if 'output_pin' in self.cleaned_data:
|
|
712
|
+
self._clean_pin('output_pin')
|
|
713
|
+
if 'controls' in self.cleaned_data:
|
|
714
|
+
self._clean_controls()
|
|
715
|
+
|
|
716
|
+
if self.cleaned_data.get('output_pin') and self.cleaned_data.get('controls'):
|
|
717
|
+
for ctrl in self.cleaned_data['controls']:
|
|
718
|
+
if not ctrl['input'].startswith('pin'):
|
|
719
|
+
continue
|
|
720
|
+
if int(ctrl['input'][4:]) == self.cleaned_data['output_pin'].id:
|
|
721
|
+
self.add_error(
|
|
722
|
+
"output_pin",
|
|
723
|
+
"Can't be used as control pin at the same time!"
|
|
724
|
+
)
|
|
725
|
+
return self.cleaned_data
|
|
726
|
+
|
|
727
|
+
|
|
728
|
+
def save(self, commit=True):
|
|
729
|
+
if 'output_pin' in self.cleaned_data:
|
|
730
|
+
self.instance.config['output_pin_no'] = self.cleaned_data['output_pin'].no
|
|
731
|
+
|
|
732
|
+
update_colonel = False
|
|
733
|
+
if not self.instance.pk:
|
|
734
|
+
update_colonel = True
|
|
735
|
+
elif 'output_pin' in self.changed_data:
|
|
736
|
+
update_colonel = True
|
|
737
|
+
elif 'slaves' in self.changed_data:
|
|
738
|
+
update_colonel = True
|
|
739
|
+
if not update_colonel:
|
|
740
|
+
old = Component.objects.get(id=self.instance.id)
|
|
741
|
+
if old.config.get('controls') != self.cleaned_data.get('controls'):
|
|
742
|
+
update_colonel = True
|
|
743
|
+
|
|
744
|
+
obj = super().save(commit=commit)
|
|
745
|
+
if commit and 'slaves' in self.cleaned_data:
|
|
746
|
+
obj.slaves.set(self.cleaned_data['slaves'])
|
|
747
|
+
if not update_colonel:
|
|
748
|
+
GatewayObjectCommand(
|
|
749
|
+
obj.gateway, self.cleaned_data['colonel'], id=obj.id,
|
|
750
|
+
command='call', method='update_config', args=[
|
|
751
|
+
obj.controller._get_colonel_config()
|
|
752
|
+
]
|
|
753
|
+
).publish()
|
|
754
|
+
if commit and self.cleaned_data.get('controls'):
|
|
755
|
+
GatewayObjectCommand(
|
|
756
|
+
self.instance.gateway, obj, command='watch_buttons'
|
|
757
|
+
).publish()
|
|
758
|
+
return obj
|
|
759
|
+
|
|
760
|
+
class ColonelPWMOutputConfigForm(PWMOutputBaseConfig):
|
|
731
761
|
output_pin = Select2ModelChoiceField(
|
|
732
762
|
label="Port",
|
|
733
763
|
queryset=ColonelPin.objects.filter(output=True),
|
|
@@ -796,74 +826,8 @@ class ColonelPWMOutputConfigForm(ColonelComponentForm):
|
|
|
796
826
|
)
|
|
797
827
|
)
|
|
798
828
|
|
|
799
|
-
def __init__(self, *args, **kwargs):
|
|
800
|
-
super().__init__(*args, **kwargs)
|
|
801
|
-
if 'value_units' in self.fields:
|
|
802
|
-
self.fields['value_units'].initial = self.controller.default_value_units
|
|
803
|
-
self.basic_fields.extend(
|
|
804
|
-
['value_units', 'turn_on_time', 'turn_off_time', 'skew']
|
|
805
|
-
)
|
|
806
|
-
if self.instance.pk and 'slaves' in self.fields:
|
|
807
|
-
self.fields['slaves'].initial = self.instance.slaves.all()
|
|
808
829
|
|
|
809
|
-
|
|
810
|
-
if not self.cleaned_data['slaves'] or not self.instance:
|
|
811
|
-
return self.cleaned_data['slaves']
|
|
812
|
-
return validate_slaves(self.cleaned_data['slaves'], self.instance)
|
|
813
|
-
|
|
814
|
-
def clean(self):
|
|
815
|
-
super().clean()
|
|
816
|
-
if 'output_pin' in self.cleaned_data:
|
|
817
|
-
self._clean_pin('output_pin')
|
|
818
|
-
if 'controls' in self.cleaned_data:
|
|
819
|
-
self._clean_controls()
|
|
820
|
-
|
|
821
|
-
if self.cleaned_data.get('output_pin') and self.cleaned_data.get('controls'):
|
|
822
|
-
for ctrl in self.cleaned_data['controls']:
|
|
823
|
-
if not ctrl['input'].startswith('pin'):
|
|
824
|
-
continue
|
|
825
|
-
if int(ctrl['input'][4:]) == self.cleaned_data['output_pin'].id:
|
|
826
|
-
self.add_error(
|
|
827
|
-
"output_pin",
|
|
828
|
-
"Can't be used as control pin at the same time!"
|
|
829
|
-
)
|
|
830
|
-
return self.cleaned_data
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
def save(self, commit=True):
|
|
834
|
-
if 'output_pin' in self.cleaned_data:
|
|
835
|
-
self.instance.config['output_pin_no'] = self.cleaned_data['output_pin'].no
|
|
836
|
-
|
|
837
|
-
update_colonel = False
|
|
838
|
-
if not self.instance.pk:
|
|
839
|
-
update_colonel = True
|
|
840
|
-
elif 'output_pin' in self.changed_data:
|
|
841
|
-
update_colonel = True
|
|
842
|
-
elif 'slaves' in self.changed_data:
|
|
843
|
-
update_colonel = True
|
|
844
|
-
if not update_colonel:
|
|
845
|
-
old = Component.objects.get(id=self.instance.id)
|
|
846
|
-
if old.config.get('controls') != self.cleaned_data.get('controls'):
|
|
847
|
-
update_colonel = True
|
|
848
|
-
|
|
849
|
-
obj = super().save(commit=commit)
|
|
850
|
-
if commit and 'slaves' in self.cleaned_data:
|
|
851
|
-
obj.slaves.set(self.cleaned_data['slaves'])
|
|
852
|
-
if not update_colonel:
|
|
853
|
-
GatewayObjectCommand(
|
|
854
|
-
obj.gateway, self.cleaned_data['colonel'], id=obj.id,
|
|
855
|
-
command='call', method='update_config', args=[
|
|
856
|
-
obj.controller._get_colonel_config()
|
|
857
|
-
]
|
|
858
|
-
).publish()
|
|
859
|
-
if commit and self.cleaned_data.get('controls'):
|
|
860
|
-
GatewayObjectCommand(
|
|
861
|
-
self.instance.gateway, obj, command='watch_buttons'
|
|
862
|
-
).publish()
|
|
863
|
-
return obj
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
class DCDriverConfigForm(ColonelComponentForm):
|
|
830
|
+
class DC10VConfigForm(PWMOutputBaseConfig):
|
|
867
831
|
output_pin = Select2ModelChoiceField(
|
|
868
832
|
label="Port",
|
|
869
833
|
queryset=ColonelPin.objects.filter(output=True),
|
|
@@ -879,53 +843,60 @@ class DCDriverConfigForm(ColonelComponentForm):
|
|
|
879
843
|
help_text="Minimum component value displayed to the user."
|
|
880
844
|
)
|
|
881
845
|
max = forms.FloatField(
|
|
882
|
-
required=True, initial=
|
|
846
|
+
required=True, initial=100,
|
|
883
847
|
help_text="Maximum component value displayed to the user."
|
|
884
848
|
)
|
|
885
|
-
value_units = forms.CharField(required=False)
|
|
849
|
+
value_units = forms.CharField(required=False, initial='%')
|
|
886
850
|
|
|
887
851
|
device_min = forms.FloatField(
|
|
888
852
|
label="Device minimum Voltage.",
|
|
889
853
|
help_text="This will be the lowest possible voltage value of a device.\n"
|
|
890
854
|
"Don't forget to adjust your component min value accordingly "
|
|
891
855
|
"if you change this.",
|
|
892
|
-
initial=0, min_value=0, max_value=
|
|
856
|
+
initial=0, min_value=0, max_value=10,
|
|
893
857
|
)
|
|
894
|
-
device_max = forms.
|
|
858
|
+
device_max = forms.FloatField(
|
|
895
859
|
label="Device maximum Voltage.",
|
|
896
|
-
help_text="Can be set lower than it's natural maximum of
|
|
860
|
+
help_text="Can be set lower than it's natural maximum of 10V. \n"
|
|
897
861
|
"Don't forget to adjust your component max value accordingly "
|
|
898
862
|
"if you change this.",
|
|
899
|
-
initial=
|
|
863
|
+
initial=10, min_value=0, max_value=10,
|
|
900
864
|
)
|
|
865
|
+
inverse = forms.BooleanField(required=False, initial=False)
|
|
901
866
|
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
867
|
+
turn_on_time = forms.IntegerField(
|
|
868
|
+
min_value=0, max_value=60000, initial=0,
|
|
869
|
+
help_text="Turn on speed in ms. 1500 is a great quick default for controlling lights. "
|
|
870
|
+
"10000 - great slow default."
|
|
871
|
+
)
|
|
872
|
+
turn_off_time = forms.IntegerField(
|
|
873
|
+
min_value=0, max_value=60000, initial=0,
|
|
874
|
+
help_text="Turn off speed in ms. 3000 is a great quick default when controlling lights. "
|
|
875
|
+
"20000 - great slow default"
|
|
876
|
+
)
|
|
877
|
+
skew = forms.ChoiceField(
|
|
878
|
+
initial='linear', choices=EASING_CHOICES,
|
|
879
|
+
help_text="easeOutSine - offers most naturally looking effect for lights."
|
|
880
|
+
)
|
|
881
|
+
on_value = forms.FloatField(
|
|
882
|
+
required=False,
|
|
883
|
+
help_text="Static ON value used to turn on the device with physical controls. <br>"
|
|
884
|
+
"Leaving this field empty turns the device on to the last used value."
|
|
885
|
+
)
|
|
920
886
|
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
887
|
+
slaves = Select2ModelMultipleChoiceField(
|
|
888
|
+
queryset=Component.objects.filter(
|
|
889
|
+
base_type__in=('dimmer',),
|
|
890
|
+
),
|
|
891
|
+
url='autocomplete-component',
|
|
892
|
+
forward=(forward.Const(['dimmer', ], 'base_type'),),
|
|
893
|
+
required=False
|
|
894
|
+
)
|
|
895
|
+
controls = FormsetField(
|
|
896
|
+
formset_factory(
|
|
897
|
+
ControlForm, can_delete=True, can_order=True, extra=0, max_num=10
|
|
898
|
+
)
|
|
899
|
+
)
|
|
929
900
|
|
|
930
901
|
|
|
931
902
|
class ColonelRGBLightConfigForm(ColonelComponentForm):
|
|
@@ -1831,8 +1802,6 @@ class CustomDaliDeviceForm(BaseComponentForm):
|
|
|
1831
1802
|
for colonel in Colonel.objects.filter(
|
|
1832
1803
|
type='room-sensor', instance=instance
|
|
1833
1804
|
):
|
|
1834
|
-
if not colonel.is_connected:
|
|
1835
|
-
continue
|
|
1836
1805
|
choices.append((f"wifi-{colonel.id}", colonel.name))
|
|
1837
1806
|
for device in CustomDaliDevice.objects.filter(
|
|
1838
1807
|
instance=instance,
|
|
@@ -1854,6 +1823,7 @@ class CustomDaliDeviceForm(BaseComponentForm):
|
|
|
1854
1823
|
|
|
1855
1824
|
class RoomSensorDeviceConfigForm(CustomDaliDeviceForm):
|
|
1856
1825
|
|
|
1826
|
+
|
|
1857
1827
|
def save(self, commit=True):
|
|
1858
1828
|
from simo.core.models import Icon
|
|
1859
1829
|
colonel = None
|
simo/fleet/gateways.py
CHANGED
|
@@ -11,6 +11,7 @@ from simo.core.utils.serialization import deserialize_form_data
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
|
|
14
|
+
|
|
14
15
|
class FleetGatewayHandler(BaseObjectCommandsGatewayHandler):
|
|
15
16
|
name = "SIMO.io Fleet"
|
|
16
17
|
config_form = BaseGatewayForm
|
|
@@ -101,15 +102,19 @@ class FleetGatewayHandler(BaseObjectCommandsGatewayHandler):
|
|
|
101
102
|
gw.finish_discovery()
|
|
102
103
|
continue
|
|
103
104
|
|
|
104
|
-
colonel = Colonel.objects.get(
|
|
105
|
-
id=gw.discovery['init_data']['colonel']['val'][0]['pk']
|
|
106
|
-
)
|
|
107
105
|
if gw.discovery['controller_uid'] == 'simo.fleet.controllers.TTLock':
|
|
106
|
+
colonel = Colonel.objects.get(
|
|
107
|
+
id=gw.discovery['init_data']['colonel']['val'][0]['pk']
|
|
108
|
+
)
|
|
108
109
|
GatewayObjectCommand(
|
|
109
110
|
gw, colonel, command='discover',
|
|
110
111
|
type=gw.discovery['controller_uid']
|
|
111
112
|
).publish()
|
|
112
|
-
elif gw.discovery['controller_uid'] ==
|
|
113
|
+
elif gw.discovery['controller_uid'] == \
|
|
114
|
+
'simo.fleet.controllers.DALIDevice':
|
|
115
|
+
colonel = Colonel.objects.get(
|
|
116
|
+
id=gw.discovery['init_data']['colonel']['val'][0]['pk']
|
|
117
|
+
)
|
|
113
118
|
form_cleaned_data = deserialize_form_data(gw.discovery['init_data'])
|
|
114
119
|
GatewayObjectCommand(
|
|
115
120
|
gw, colonel,
|
|
@@ -117,6 +122,31 @@ class FleetGatewayHandler(BaseObjectCommandsGatewayHandler):
|
|
|
117
122
|
type=gw.discovery['controller_uid'],
|
|
118
123
|
i=form_cleaned_data['interface'].no
|
|
119
124
|
).publish()
|
|
125
|
+
elif gw.discovery['controller_uid'] == \
|
|
126
|
+
'simo.fleet.controllers.RoomZonePresenceSensor':
|
|
127
|
+
form_cleaned_data = deserialize_form_data(
|
|
128
|
+
gw.discovery['init_data']
|
|
129
|
+
)
|
|
130
|
+
if form_cleaned_data['device'].startswith('wifi'):
|
|
131
|
+
colonel = Colonel.objects.filter(
|
|
132
|
+
id=form_cleaned_data['device'][5:]
|
|
133
|
+
).first()
|
|
134
|
+
GatewayObjectCommand(
|
|
135
|
+
gw, colonel,
|
|
136
|
+
command='discover', type=self.uid.split('.')[-1],
|
|
137
|
+
).publish()
|
|
138
|
+
else:
|
|
139
|
+
from .models import CustomDaliDevice
|
|
140
|
+
from .custom_dali_operations import Frame
|
|
141
|
+
dali_device = CustomDaliDevice.objects.filter(
|
|
142
|
+
id=form_cleaned_data['device'][5:]
|
|
143
|
+
).first()
|
|
144
|
+
frame = Frame(40, bytes(bytearray(5)))
|
|
145
|
+
frame[8:11] = 15 # command to custom dali device
|
|
146
|
+
frame[12:15] = 0 # action to perform: start room zone discovery
|
|
147
|
+
dali_device.transmit(frame)
|
|
148
|
+
|
|
149
|
+
|
|
120
150
|
|
|
121
151
|
def watch_buttons(self, component):
|
|
122
152
|
for i, ctrl in enumerate(component.config.get('controls', [])):
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Generated by Django 4.2.10 on 2025-05-07 07:13
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
('fleet', '0051_customdalidevice_components'),
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
operations = [
|
|
13
|
+
migrations.AddField(
|
|
14
|
+
model_name='colonelpin',
|
|
15
|
+
name='interface',
|
|
16
|
+
field=models.PositiveIntegerField(blank=True, null=True),
|
|
17
|
+
),
|
|
18
|
+
]
|
|
@@ -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
|
+
]
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Generated by Django 4.2.10 on 2025-05-07 12:56
|
|
2
|
+
|
|
3
|
+
from django.db import migrations
|
|
4
|
+
|
|
5
|
+
def forwards_func(apps, schema_editor):
|
|
6
|
+
Component = apps.get_model("core", "Component")
|
|
7
|
+
ColonelPin = apps.get_model('fleet', "ColonelPin")
|
|
8
|
+
|
|
9
|
+
for comp in Component.objects.filter(controller_uid__in=(
|
|
10
|
+
'simo.fleet.controllers.BME680Sensor',
|
|
11
|
+
'simo.fleet.controllers.MCP9808TempSensor',
|
|
12
|
+
'simo.fleet.controllers.ENS160AirQualitySensor'
|
|
13
|
+
)):
|
|
14
|
+
cp = ColonelPin.objects.filter(
|
|
15
|
+
colonel__id=comp.config['colonel'],
|
|
16
|
+
interface=comp.config['i2c_interface']
|
|
17
|
+
).first()
|
|
18
|
+
if cp:
|
|
19
|
+
comp.config['interface_port'] = cp.id
|
|
20
|
+
comp.save()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def reverse_func(apps, schema_editor):
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class Migration(migrations.Migration):
|
|
29
|
+
|
|
30
|
+
dependencies = [
|
|
31
|
+
('fleet', '0053_auto_20250507_0713'),
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
operations = [
|
|
35
|
+
migrations.RunPython(forwards_func, reverse_func, elidable=True),
|
|
36
|
+
]
|
|
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
|