simo 2.0.32__py3-none-any.whl → 2.0.33__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.

Files changed (60) hide show
  1. simo/cli.py +2 -17
  2. simo/core/__pycache__/admin.cpython-38.pyc +0 -0
  3. simo/core/__pycache__/api.cpython-38.pyc +0 -0
  4. simo/core/__pycache__/controllers.cpython-38.pyc +0 -0
  5. simo/core/__pycache__/forms.cpython-38.pyc +0 -0
  6. simo/core/__pycache__/managers.cpython-38.pyc +0 -0
  7. simo/core/__pycache__/models.cpython-38.pyc +0 -0
  8. simo/core/__pycache__/serializers.cpython-38.pyc +0 -0
  9. simo/core/__pycache__/signal_receivers.cpython-38.pyc +0 -0
  10. simo/core/__pycache__/tasks.cpython-38.pyc +0 -0
  11. simo/core/admin.py +2 -2
  12. simo/core/api.py +25 -2
  13. simo/core/controllers.py +11 -7
  14. simo/core/forms.py +1 -2
  15. simo/core/managers.py +1 -5
  16. simo/core/migrations/0003_create_default_zones_and_categories.py +2 -39
  17. simo/core/migrations/0004_create_generic.py +22 -21
  18. simo/core/migrations/0013_auto_20231003_0754.py +45 -43
  19. simo/core/migrations/0018_auto_20231005_0622.py +18 -16
  20. simo/core/migrations/0033_auto_20240509_0821.py +25 -0
  21. simo/core/migrations/0034_component_error_msg.py +18 -0
  22. simo/core/migrations/0035_remove_instance_share_location.py +17 -0
  23. simo/core/migrations/__pycache__/0003_create_default_zones_and_categories.cpython-38.pyc +0 -0
  24. simo/core/migrations/__pycache__/0004_create_generic.cpython-38.pyc +0 -0
  25. simo/core/migrations/__pycache__/0013_auto_20231003_0754.cpython-38.pyc +0 -0
  26. simo/core/migrations/__pycache__/0018_auto_20231005_0622.cpython-38.pyc +0 -0
  27. simo/core/migrations/__pycache__/0033_auto_20240509_0821.cpython-38.pyc +0 -0
  28. simo/core/migrations/__pycache__/0034_component_error_msg.cpython-38.pyc +0 -0
  29. simo/core/migrations/__pycache__/0035_remove_instance_share_location.cpython-38.pyc +0 -0
  30. simo/core/models.py +25 -23
  31. simo/core/serializers.py +5 -3
  32. simo/core/signal_receivers.py +82 -1
  33. simo/core/tasks.py +7 -4
  34. simo/fleet/__pycache__/admin.cpython-38.pyc +0 -0
  35. simo/fleet/__pycache__/controllers.cpython-38.pyc +0 -0
  36. simo/fleet/__pycache__/forms.cpython-38.pyc +0 -0
  37. simo/fleet/__pycache__/models.cpython-38.pyc +0 -0
  38. simo/fleet/__pycache__/serializers.cpython-38.pyc +0 -0
  39. simo/fleet/__pycache__/socket_consumers.cpython-38.pyc +0 -0
  40. simo/fleet/admin.py +25 -6
  41. simo/fleet/controllers.py +82 -37
  42. simo/fleet/forms.py +142 -10
  43. simo/fleet/migrations/0035_auto_20240514_0855.py +32 -0
  44. simo/fleet/migrations/__pycache__/0035_auto_20240514_0855.cpython-38.pyc +0 -0
  45. simo/fleet/models.py +96 -82
  46. simo/fleet/serializers.py +8 -1
  47. simo/fleet/socket_consumers.py +3 -15
  48. simo/users/__pycache__/models.cpython-38.pyc +0 -0
  49. simo/users/__pycache__/utils.cpython-38.pyc +0 -0
  50. simo/users/migrations/0003_create_roles_and_system_user.py +24 -23
  51. simo/users/migrations/0019_auto_20231221_1155.py +9 -8
  52. simo/users/migrations/__pycache__/0003_create_roles_and_system_user.cpython-38.pyc +0 -0
  53. simo/users/migrations/__pycache__/0019_auto_20231221_1155.cpython-38.pyc +0 -0
  54. simo/users/models.py +6 -7
  55. simo/users/utils.py +0 -4
  56. {simo-2.0.32.dist-info → simo-2.0.33.dist-info}/METADATA +1 -1
  57. {simo-2.0.32.dist-info → simo-2.0.33.dist-info}/RECORD +60 -52
  58. {simo-2.0.32.dist-info → simo-2.0.33.dist-info}/LICENSE.md +0 -0
  59. {simo-2.0.32.dist-info → simo-2.0.33.dist-info}/WHEEL +0 -0
  60. {simo-2.0.32.dist-info → simo-2.0.33.dist-info}/top_level.txt +0 -0
simo/cli.py CHANGED
@@ -3,48 +3,35 @@ import sys
3
3
  import click
4
4
  import os
5
5
  import shutil
6
- import requests
7
- from subprocess import Popen, PIPE
8
6
  import simo
9
7
  from django.template import Context, Engine
10
8
  from django.core.management.utils import get_random_secret_key
11
9
 
12
10
 
13
- template_file_extensions = ['.py', '.conf']
14
-
15
-
16
11
  def copy_default_template(to_directory='/etc/SIMO'):
12
+ template_file_extensions = ['.py', '.conf']
13
+
17
14
  context = Context({
18
15
  'secret_key': get_random_secret_key(),
19
16
  'project_dir': to_directory,
20
17
  'base_dir': to_directory
21
18
  }, autoescape=False)
22
-
23
-
24
19
  template_dir = os.path.join(
25
20
  os.path.dirname(simo.__file__), '_hub_template'
26
21
  )
27
-
28
22
  prefix_length = len(template_dir) + 1
29
-
30
23
  for root, dirs, files in os.walk(template_dir):
31
-
32
24
  relative_dir = root[prefix_length:]
33
25
  target_dir = os.path.join(to_directory, relative_dir)
34
26
  os.makedirs(target_dir, exist_ok=True)
35
-
36
27
  for filename in files:
37
28
  if filename.endswith(('.pyo', '.pyc', '.py.class')):
38
29
  # Ignore some files as they cause various breakages.
39
30
  continue
40
-
41
31
  old_path = os.path.join(root, filename)
42
32
  new_path = os.path.join(target_dir, filename)
43
-
44
33
  os.makedirs(target_dir, exist_ok=True)
45
-
46
34
  fn, file_extension = os.path.splitext(new_path)
47
-
48
35
  if file_extension in template_file_extensions:
49
36
  with open(old_path, encoding='utf-8') as template_file:
50
37
  content = template_file.read()
@@ -54,9 +41,7 @@ def copy_default_template(to_directory='/etc/SIMO'):
54
41
  new_file.write(content)
55
42
  else:
56
43
  shutil.copyfile(old_path, new_path)
57
-
58
44
  shutil.copymode(old_path, new_path)
59
-
60
45
  for dirname in dirs[:]:
61
46
  if dirname.startswith('.') or dirname == '__pycache__':
62
47
  dirs.remove(dirname)
Binary file
Binary file
Binary file
Binary file
Binary file
simo/core/admin.py CHANGED
@@ -261,8 +261,8 @@ class ComponentAdmin(admin.ModelAdmin):
261
261
  'alarm_category', 'show_in_app',
262
262
  )
263
263
  readonly_fields = (
264
- 'id', 'controller_uid', 'base_type', 'gateway', 'config', 'alive',
265
- 'battery_level',
264
+ 'id', 'controller_uid', 'base_type', 'gateway', 'config',
265
+ 'alive', 'error_msg', 'battery_level',
266
266
  'control', 'value', 'arm_status', 'history', 'meta'
267
267
  )
268
268
  list_filter = (
simo/core/api.py CHANGED
@@ -125,6 +125,22 @@ class ZoneViewSet(InstanceMixin, viewsets.ModelViewSet):
125
125
  serializer.validated_data['instance'] = self.instance
126
126
  serializer.save()
127
127
 
128
+ @action(detail=False, methods=['post'])
129
+ def reorder(self, request, pk=None, *args, **kwargs):
130
+ data = request.data
131
+ if not isinstance(request.data, dict):
132
+ data = data.dict()
133
+ request_data = restore_json(data)
134
+ zones = {str(z.id): z for z in Zone.objects.filter(instance=self.instance)}
135
+ if len(request_data.get('zones', [])) != len(zones):
136
+ raise APIValidationError(
137
+ _('All zones must be provided to perform reorder.'), code=400
138
+ )
139
+ for i, id in enumerate(request_data.get('zones')):
140
+ zones[str(id)].order = i
141
+ Zone.objects.bulk_update([z for id, z in zones.items()], fields=['order'])
142
+ return RESTResponse({'status': 'success'})
143
+
128
144
 
129
145
  def get_components_queryset(instance, user):
130
146
  qs = Component.objects.filter(zone__instance=instance)
@@ -552,15 +568,22 @@ class StatesViewSet(InstanceMixin, viewsets.GenericViewSet):
552
568
  component_values = get_components_queryset(
553
569
  self.instance, request.user
554
570
  ).filter(zone__instance=self.instance).values(
555
- 'id', 'value', 'last_change', 'arm_status', 'battery_level',
556
- 'alive', 'meta'
571
+ 'id', 'value', 'last_change', 'last_modified',
572
+ 'arm_status', 'battery_level', 'alive', 'error_msg',
573
+ 'meta'
557
574
  )
558
575
  for vals in component_values:
559
576
  vals['last_change'] = datetime.datetime.timestamp(
560
577
  vals['last_change']
561
578
  )
579
+ vals['last_modified'] = datetime.datetime.timestamp(
580
+ vals['last_modified']
581
+ )
562
582
 
563
583
  return RESTResponse({
584
+ 'zones': Zone.objects.filter(instance=self.instance).values(
585
+ 'id', 'name'
586
+ ),
564
587
  'component_values': component_values,
565
588
  'users': UserSerializer(
566
589
  users_qs, many=True, context={
simo/core/controllers.py CHANGED
@@ -35,6 +35,7 @@ class ControllerBase(ABC):
35
35
  default_meta = {}
36
36
  discovery_msg = None
37
37
  manual_add = True # Can be added manually
38
+ family = None
38
39
 
39
40
  @property
40
41
  @abstractmethod
@@ -187,8 +188,6 @@ class ControllerBase(ABC):
187
188
  bulk_send_map = {self.component: value}
188
189
  for slave in self.component.slaves.all():
189
190
  bulk_send_map[slave] = value
190
-
191
- print("BULK SEND MAP: ", bulk_send_map)
192
191
  from .models import Component
193
192
  Component.objects.bulk_send(bulk_send_map)
194
193
  return
@@ -232,7 +231,9 @@ class ControllerBase(ABC):
232
231
  self.component.change_init_fingerprint = None
233
232
  self.component.save()
234
233
 
235
- def _receive_from_device(self, value, is_alive=True, battery_level=None):
234
+ def _receive_from_device(
235
+ self, value, is_alive=True, battery_level=None, error_msg=None
236
+ ):
236
237
  value = self._prepare_for_set(value)
237
238
  actor = self._get_actor(value)
238
239
 
@@ -245,9 +246,11 @@ class ControllerBase(ABC):
245
246
  # in relation to the change of this component
246
247
  introduce(actor)
247
248
  self.component.alive = is_alive
249
+ if error_msg != None:
250
+ self.component.error_msg = error_msg if error_msg.strip() else None
248
251
  if battery_level:
249
252
  self.component.battery_level = battery_level
250
- self.component.save(update_fields=['alive', 'battery_level'])
253
+ self.component.save(update_fields=['alive', 'battery_level', 'error_msg'])
251
254
  self.set(value, actor)
252
255
 
253
256
  if init_by_device and self.component.slaves.count():
@@ -466,7 +469,6 @@ class Dimmer(ControllerBase, TimerMixin, OnOffPokerMixin):
466
469
  default_config = {'min': 0.0, 'max': 100.0, 'inverse': False}
467
470
  default_value = 0
468
471
 
469
-
470
472
  def _prepare_for_send(self, value):
471
473
  if isinstance(value, bool):
472
474
  if value:
@@ -772,7 +774,9 @@ class Lock(Switch):
772
774
  def unlock(self):
773
775
  self.turn_off()
774
776
 
775
- def _receive_from_device(self, value, is_alive=True, battery_level=None):
777
+ def _receive_from_device(
778
+ self, value, *args, **kwargs
779
+ ):
776
780
  if type(value) == bool:
777
781
  if value:
778
782
  value = 'locked'
@@ -788,7 +792,7 @@ class Lock(Switch):
788
792
  }
789
793
  value = values_map.get(value, 'fault')
790
794
  return super()._receive_from_device(
791
- value, is_alive=is_alive, battery_level=battery_level
795
+ value, *args, **kwargs
792
796
  )
793
797
 
794
798
  def _validate_val(self, value, occasion=None):
simo/core/forms.py CHANGED
@@ -393,7 +393,7 @@ class ComponentAdminForm(forms.ModelForm):
393
393
  fieldsets.extend([
394
394
  (_("Meta"), {
395
395
  'fields': (
396
- 'alive', 'battery_level',
396
+ 'alive', 'error_msg', 'battery_level',
397
397
  'config', 'meta',
398
398
  'value', 'value_units',
399
399
  'history'
@@ -684,7 +684,6 @@ class DimmerConfigForm(BaseComponentForm):
684
684
  return validate_slaves(self.cleaned_data['slaves'], self.instance)
685
685
 
686
686
  def save(self, commit=True):
687
- self.instance.config['output_pin_no'] = self.cleaned_data['output_pin'].no
688
687
  obj = super().save(commit=commit)
689
688
  if commit:
690
689
  obj.slaves.set(self.cleaned_data['slaves'])
simo/core/managers.py CHANGED
@@ -71,11 +71,7 @@ class ComponentsManager(models.Manager):
71
71
  gateway_components[comp.gateway] = {}
72
72
  gateway_components[comp.gateway][comp.id] = value
73
73
 
74
- print("BULK SEND: ", gateway_components)
75
74
  for gateway, send_vals in gateway_components.items():
76
75
  GatewayObjectCommand(gateway, bulk_send=send_vals).publish(
77
76
  retain=False
78
- )
79
-
80
-
81
-
77
+ )
@@ -1,47 +1,10 @@
1
1
  # Generated by Django 2.2.12 on 2021-11-04 14:10
2
- import os
3
- import shutil
4
2
  from django.db import migrations
5
- from django.conf import settings
6
3
 
7
4
 
8
5
  def create_objects(apps, schema_editor):
9
-
10
- core_dir_path = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
11
- imgs_folder = os.path.join(
12
- core_dir_path, 'static/defaults/category_headers'
13
- )
14
-
15
- Icon = apps.get_model("core", "Icon")
16
- Zone = apps.get_model("core", "Zone")
17
- Category = apps.get_model("core", "Category")
18
-
19
- for zone_name in (
20
- 'Living Room', 'Kitchen', 'Bathroom', 'Porch', 'Garage', 'Yard', 'Other'
21
- ):
22
- Zone.objects.create(name=zone_name)
23
-
24
- categories_media_dir = os.path.join(settings.MEDIA_ROOT, 'categories')
25
- if not os.path.exists(categories_media_dir):
26
- os.makedirs(categories_media_dir)
27
-
28
- for i, data in enumerate([
29
- ("All", 'star'), ("Climate", 'temperature-half'),
30
- ("Lights", 'lightbulb'), ("Security", 'eye'),
31
- ("Watering", 'faucet'), ("Other", 'flag-pennant')
32
- ]):
33
- shutil.copy(
34
- os.path.join(imgs_folder, "%s.jpg" % data[0].lower()),
35
- os.path.join(
36
- settings.MEDIA_ROOT, 'categories', "%s.jpg" % data[0].lower()
37
- )
38
- )
39
- Category.objects.create(
40
- name=data[0], icon=Icon.objects.get(slug=data[1]),
41
- all=i==0, header_image=os.path.join(
42
- 'categories', "%s.jpg" % data[0].lower()
43
- ), order=i+10
44
- )
6
+ # legacy..
7
+ pass
45
8
 
46
9
 
47
10
  def delete_objects(apps, schema_editor):
@@ -4,27 +4,28 @@ from django.db import migrations
4
4
 
5
5
 
6
6
  def create_objects(apps, schema_editor):
7
-
8
- Icon = apps.get_model("core", "Icon")
9
- Zone = apps.get_model("core", "Zone")
10
- Category = apps.get_model("core", "Category")
11
- Gateway = apps.get_model("core", "Gateway")
12
- Component = apps.get_model("core", "Component")
13
-
14
- generic, new = Gateway.objects.get_or_create(
15
- type='simo.generic.gateways.GenericGatewayHandler'
16
- )
17
- weather_icon = Icon.objects.get(slug='cloud-bolt-sun')
18
- other_zone = Zone.objects.get(name='Other')
19
- climate_category = Category.objects.get(name='Climate')
20
- Component.objects.create(
21
- name='Weather', icon=weather_icon,
22
- zone=other_zone,
23
- category=climate_category,
24
- gateway=generic, base_type='weather-forecast',
25
- controller_uid='simo.generic.controllers.WeatherForecast',
26
- config={'is_main': True}
27
- )
7
+ pass
8
+ # legacy
9
+ # Icon = apps.get_model("core", "Icon")
10
+ # Zone = apps.get_model("core", "Zone")
11
+ # Category = apps.get_model("core", "Category")
12
+ # Gateway = apps.get_model("core", "Gateway")
13
+ # Component = apps.get_model("core", "Component")
14
+ #
15
+ # generic, new = Gateway.objects.get_or_create(
16
+ # type='simo.generic.gateways.GenericGatewayHandler'
17
+ # )
18
+ # weather_icon = Icon.objects.get(slug='cloud-bolt-sun')
19
+ # other_zone = Zone.objects.get(name='Other')
20
+ # climate_category = Category.objects.get(name='Climate')
21
+ # Component.objects.create(
22
+ # name='Weather', icon=weather_icon,
23
+ # zone=other_zone,
24
+ # category=climate_category,
25
+ # gateway=generic, base_type='weather-forecast',
26
+ # controller_uid='simo.generic.controllers.WeatherForecast',
27
+ # config={'is_main': True}
28
+ # )
28
29
 
29
30
 
30
31
  def delete_objects(apps, schema_editor):
@@ -3,49 +3,51 @@ from django.db import migrations
3
3
 
4
4
 
5
5
  def forwards_func(apps, schema_editor):
6
- Instance = apps.get_model("core", "Instance")
7
- Component = apps.get_model("core", "Component")
8
- GlobalPreferenceModel = apps.get_model(
9
- 'dynamic_preferences', "GlobalPreferenceModel"
10
- )
11
-
12
- Instance.objects.create(
13
- uid=GlobalPreferenceModel.objects.get(
14
- section='core', name='hub_uid'
15
- ).raw_value,
16
- name=GlobalPreferenceModel.objects.get(
17
- section='core', name='hub_name'
18
- ).raw_value,
19
- cover_image=GlobalPreferenceModel.objects.get(
20
- section='core', name='cover_image'
21
- ).raw_value,
22
- cover_image_synced=True,
23
- secret_key=GlobalPreferenceModel.objects.get(
24
- section='core', name='hub_secret'
25
- ).raw_value,
26
- location=GlobalPreferenceModel.objects.get(
27
- section='core', name='location_coordinates'
28
- ).raw_value,
29
- timezone=GlobalPreferenceModel.objects.get(
30
- section='core', name='time_zone'
31
- ).raw_value,
32
- units_of_measure=GlobalPreferenceModel.objects.get(
33
- section='core', name='units_of_measure'
34
- ).raw_value,
35
- share_location=True,
36
- remote_http=GlobalPreferenceModel.objects.get(
37
- section='core', name='remote_http'
38
- ).raw_value,
39
- remote_connection_version=int(GlobalPreferenceModel.objects.get(
40
- section='core', name='remote_conn_version'
41
- ).raw_value),
42
- indoor_climate_sensor=Component(id=int(GlobalPreferenceModel.objects.get(
43
- section='core', name='indoor_climate_sensor'
44
- ).raw_value)),
45
- history_days=int(GlobalPreferenceModel.objects.get(
46
- section='core', name='history_days'
47
- ).raw_value)
48
- )
6
+ pass
7
+ # legacy
8
+ # Instance = apps.get_model("core", "Instance")
9
+ # Component = apps.get_model("core", "Component")
10
+ # GlobalPreferenceModel = apps.get_model(
11
+ # 'dynamic_preferences', "GlobalPreferenceModel"
12
+ # )
13
+ #
14
+ # Instance.objects.create(
15
+ # uid=GlobalPreferenceModel.objects.get(
16
+ # section='core', name='hub_uid'
17
+ # ).raw_value,
18
+ # name=GlobalPreferenceModel.objects.get(
19
+ # section='core', name='hub_name'
20
+ # ).raw_value,
21
+ # cover_image=GlobalPreferenceModel.objects.get(
22
+ # section='core', name='cover_image'
23
+ # ).raw_value,
24
+ # cover_image_synced=True,
25
+ # secret_key=GlobalPreferenceModel.objects.get(
26
+ # section='core', name='hub_secret'
27
+ # ).raw_value,
28
+ # location=GlobalPreferenceModel.objects.get(
29
+ # section='core', name='location_coordinates'
30
+ # ).raw_value,
31
+ # timezone=GlobalPreferenceModel.objects.get(
32
+ # section='core', name='time_zone'
33
+ # ).raw_value,
34
+ # units_of_measure=GlobalPreferenceModel.objects.get(
35
+ # section='core', name='units_of_measure'
36
+ # ).raw_value,
37
+ # share_location=True,
38
+ # remote_http=GlobalPreferenceModel.objects.get(
39
+ # section='core', name='remote_http'
40
+ # ).raw_value,
41
+ # remote_connection_version=int(GlobalPreferenceModel.objects.get(
42
+ # section='core', name='remote_conn_version'
43
+ # ).raw_value),
44
+ # indoor_climate_sensor=Component(id=int(GlobalPreferenceModel.objects.get(
45
+ # section='core', name='indoor_climate_sensor'
46
+ # ).raw_value)),
47
+ # history_days=int(GlobalPreferenceModel.objects.get(
48
+ # section='core', name='history_days'
49
+ # ).raw_value)
50
+ # )
49
51
 
50
52
 
51
53
  def reverse_func(apps, schema_editor):
@@ -4,22 +4,24 @@ from django.db import migrations
4
4
 
5
5
 
6
6
  def forwards_func(apps, schema_editor):
7
- Instance = apps.get_model("core", "Instance")
8
- GlobalPreferenceModel = apps.get_model(
9
- 'dynamic_preferences', "GlobalPreferenceModel"
10
- )
11
-
12
- instance = Instance.objects.all().first()
13
- GlobalPreferenceModel.objects.update_or_create(
14
- section='core', name='hub_uid', defaults={
15
- 'raw_value': instance.uid
16
- }
17
- )
18
- GlobalPreferenceModel.objects.update_or_create(
19
- section='core', name='hub_secret', defaults={
20
- 'raw_value': instance.secret_key
21
- }
22
- )
7
+ pass
8
+ # legacy
9
+ # Instance = apps.get_model("core", "Instance")
10
+ # GlobalPreferenceModel = apps.get_model(
11
+ # 'dynamic_preferences', "GlobalPreferenceModel"
12
+ # )
13
+ #
14
+ # instance = Instance.objects.all().first()
15
+ # GlobalPreferenceModel.objects.update_or_create(
16
+ # section='core', name='hub_uid', defaults={
17
+ # 'raw_value': instance.uid
18
+ # }
19
+ # )
20
+ # GlobalPreferenceModel.objects.update_or_create(
21
+ # section='core', name='hub_secret', defaults={
22
+ # 'raw_value': instance.secret_key
23
+ # }
24
+ # )
23
25
 
24
26
  def reverse_func(apps, schema_editor):
25
27
  pass
@@ -0,0 +1,25 @@
1
+ # Generated by Django 3.2.9 on 2024-05-09 08:21
2
+
3
+ from django.db import migrations, models
4
+ import django.utils.timezone
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+
9
+ dependencies = [
10
+ ('core', '0032_auto_20240506_0834'),
11
+ ]
12
+
13
+ operations = [
14
+ migrations.AddField(
15
+ model_name='component',
16
+ name='last_modified',
17
+ field=models.DateTimeField(auto_now_add=True, db_index=True, default=django.utils.timezone.now, help_text='Last time component was modified.'),
18
+ preserve_default=False,
19
+ ),
20
+ migrations.AlterField(
21
+ model_name='component',
22
+ name='last_change',
23
+ field=models.DateTimeField(auto_now_add=True, help_text='Last time component state was changed.', null=True),
24
+ ),
25
+ ]
@@ -0,0 +1,18 @@
1
+ # Generated by Django 3.2.9 on 2024-05-09 10:25
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ('core', '0033_auto_20240509_0821'),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AddField(
14
+ model_name='component',
15
+ name='error_msg',
16
+ field=models.TextField(blank=True, editable=False, null=True),
17
+ ),
18
+ ]
@@ -0,0 +1,17 @@
1
+ # Generated by Django 3.2.9 on 2024-05-20 11:46
2
+
3
+ from django.db import migrations
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ('core', '0034_component_error_msg'),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.RemoveField(
14
+ model_name='instance',
15
+ name='share_location',
16
+ ),
17
+ ]