simo 2.4.1__py3-none-any.whl → 2.5.1__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 (82) hide show
  1. simo/backups/__pycache__/admin.cpython-38.pyc +0 -0
  2. simo/backups/__pycache__/models.cpython-38.pyc +0 -0
  3. simo/backups/__pycache__/tasks.cpython-38.pyc +0 -0
  4. simo/backups/admin.py +10 -13
  5. simo/backups/migrations/0003_alter_backuplog_options_alter_backup_size.py +22 -0
  6. simo/backups/migrations/0004_alter_backup_options_alter_backuplog_options_and_more.py +29 -0
  7. simo/backups/migrations/__pycache__/0003_alter_backuplog_options_alter_backup_size.cpython-38.pyc +0 -0
  8. simo/backups/migrations/__pycache__/0004_alter_backup_options_alter_backuplog_options_and_more.cpython-38.pyc +0 -0
  9. simo/backups/models.py +1 -7
  10. simo/backups/tasks.py +221 -145
  11. simo/core/__pycache__/admin.cpython-38.pyc +0 -0
  12. simo/core/__pycache__/api.cpython-38.pyc +0 -0
  13. simo/core/__pycache__/app_widgets.cpython-38.pyc +0 -0
  14. simo/core/__pycache__/controllers.cpython-38.pyc +0 -0
  15. simo/core/__pycache__/events.cpython-38.pyc +0 -0
  16. simo/core/__pycache__/forms.cpython-38.pyc +0 -0
  17. simo/core/__pycache__/middleware.cpython-38.pyc +0 -0
  18. simo/core/__pycache__/models.cpython-38.pyc +0 -0
  19. simo/core/__pycache__/serializers.cpython-38.pyc +0 -0
  20. simo/core/__pycache__/tasks.cpython-38.pyc +0 -0
  21. simo/core/admin.py +4 -4
  22. simo/core/api.py +20 -4
  23. simo/core/app_widgets.py +5 -0
  24. simo/core/controllers.py +2 -2
  25. simo/core/events.py +2 -0
  26. simo/core/forms.py +2 -0
  27. simo/core/management/commands/gateways_manager.py +0 -3
  28. simo/core/middleware.py +7 -1
  29. simo/core/migrations/0042_alter_instance_timezone.py +18 -0
  30. simo/core/migrations/__pycache__/0042_alter_instance_timezone.cpython-38.pyc +0 -0
  31. simo/core/models.py +26 -6
  32. simo/core/serializers.py +17 -17
  33. simo/core/tasks.py +10 -7
  34. simo/fleet/__pycache__/controllers.cpython-38.pyc +0 -0
  35. simo/fleet/__pycache__/forms.cpython-38.pyc +0 -0
  36. simo/fleet/__pycache__/models.cpython-38.pyc +0 -0
  37. simo/fleet/controllers.py +86 -22
  38. simo/fleet/forms.py +224 -185
  39. simo/fleet/migrations/0038_alter_colonel_type.py +18 -0
  40. simo/fleet/migrations/0039_auto_20241016_1047.py +28 -0
  41. simo/fleet/migrations/0040_alter_colonel_pwm_frequency.py +18 -0
  42. simo/fleet/migrations/__pycache__/0038_alter_colonel_type.cpython-38.pyc +0 -0
  43. simo/fleet/migrations/__pycache__/0039_auto_20241016_1047.cpython-38.pyc +0 -0
  44. simo/fleet/migrations/__pycache__/0040_alter_colonel_pwm_frequency.cpython-38.pyc +0 -0
  45. simo/fleet/models.py +2 -2
  46. simo/generic/__pycache__/controllers.cpython-38.pyc +0 -0
  47. simo/generic/__pycache__/forms.cpython-38.pyc +0 -0
  48. simo/generic/__pycache__/gateways.cpython-38.pyc +0 -0
  49. simo/generic/__pycache__/models.cpython-38.pyc +0 -0
  50. simo/generic/controllers.py +41 -2
  51. simo/generic/forms.py +71 -7
  52. simo/generic/models.py +0 -1
  53. simo/generic/scripting/__init__.py +16 -0
  54. simo/generic/scripting/__pycache__/__init__.cpython-38.pyc +0 -0
  55. simo/generic/scripting/__pycache__/serializers.cpython-38.pyc +0 -0
  56. simo/generic/scripting/helpers.py +35 -0
  57. simo/generic/scripting/serializers.py +77 -0
  58. simo/generic/templates/admin/controller_widgets/weather_forecast.html +2 -2
  59. simo/notifications/__pycache__/models.cpython-38.pyc +0 -0
  60. simo/notifications/__pycache__/utils.cpython-38.pyc +0 -0
  61. simo/notifications/utils.py +30 -12
  62. simo/scripting.py +2 -2
  63. simo/users/__pycache__/api.cpython-38.pyc +0 -0
  64. simo/users/__pycache__/managers.cpython-38.pyc +0 -0
  65. simo/users/__pycache__/models.cpython-38.pyc +0 -0
  66. simo/users/__pycache__/serializers.cpython-38.pyc +0 -0
  67. simo/users/__pycache__/utils.cpython-38.pyc +0 -0
  68. simo/users/api.py +36 -7
  69. simo/users/managers.py +5 -1
  70. simo/users/migrations/0033_alter_user_ssh_key.py +18 -0
  71. simo/users/migrations/0034_instanceuser_last_seen_location_and_more.py +24 -0
  72. simo/users/migrations/__pycache__/0033_alter_user_ssh_key.cpython-38.pyc +0 -0
  73. simo/users/migrations/__pycache__/0034_instanceuser_last_seen_location_and_more.cpython-38.pyc +0 -0
  74. simo/users/models.py +37 -32
  75. simo/users/serializers.py +11 -8
  76. simo/users/utils.py +14 -3
  77. {simo-2.4.1.dist-info → simo-2.5.1.dist-info}/METADATA +1 -1
  78. {simo-2.4.1.dist-info → simo-2.5.1.dist-info}/RECORD +82 -59
  79. {simo-2.4.1.dist-info → simo-2.5.1.dist-info}/WHEEL +1 -1
  80. {simo-2.4.1.dist-info → simo-2.5.1.dist-info}/LICENSE.md +0 -0
  81. {simo-2.4.1.dist-info → simo-2.5.1.dist-info}/entry_points.txt +0 -0
  82. {simo-2.4.1.dist-info → simo-2.5.1.dist-info}/top_level.txt +0 -0
simo/core/api.py CHANGED
@@ -167,6 +167,12 @@ def get_components_queryset(instance, user):
167
167
  ).values('id').first()
168
168
  if main_alarm_group:
169
169
  c_ids.add(main_alarm_group['id'])
170
+ state = Component.objects.filter(
171
+ zone__instance=instance,
172
+ base_type='state-select', config__is_main=True
173
+ ).values('id').first()
174
+ if state:
175
+ c_ids.add(state['id'])
170
176
 
171
177
  user_role = user.get_role(instance)
172
178
 
@@ -369,7 +375,7 @@ class ComponentHistoryViewSet(InstanceMixin, viewsets.ReadOnlyModelViewSet):
369
375
  if not component.controller:
370
376
  return
371
377
 
372
- history_display_example = component.controller.history_display([component.value])
378
+ history_display_example = component.controller._history_display([component.value])
373
379
  if not history_display_example:
374
380
  return None
375
381
 
@@ -393,9 +399,9 @@ class ComponentHistoryViewSet(InstanceMixin, viewsets.ReadOnlyModelViewSet):
393
399
  values = []
394
400
  for item in history_items:
395
401
  values.append(item.value)
396
- val = component.controller.history_display(values)
402
+ val = component.controller._history_display(values)
397
403
  else:
398
- val = component.controller.history_display([])
404
+ val = component.controller._history_display([])
399
405
 
400
406
  if not val:
401
407
  val = prev_val
@@ -425,7 +431,7 @@ class ComponentHistoryViewSet(InstanceMixin, viewsets.ReadOnlyModelViewSet):
425
431
  component=component, date__lt=start_from, type='value'
426
432
  ).order_by('date').last()
427
433
  if last_event:
428
- prev_val = component.controller.history_display([last_event.value])
434
+ prev_val = component.controller._history_display([last_event.value])
429
435
  else:
430
436
  prev_val = history_display_example
431
437
 
@@ -544,6 +550,15 @@ class SettingsViewSet(InstanceMixin, viewsets.GenericViewSet):
544
550
  if main_alarm_group:
545
551
  main_alarm_group_id = main_alarm_group.id
546
552
 
553
+ main_state = Component.objects.filter(
554
+ zone__instance=self.instance,
555
+ base_type='state-select', config__is_main=True
556
+ ).first()
557
+ if main_state:
558
+ main_state = main_state.id
559
+ else:
560
+ main_state = None
561
+
547
562
  return RESTResponse({
548
563
  'hub_uid': dynamic_settings['core__hub_uid'],
549
564
  'instance_name': self.instance.name,
@@ -553,6 +568,7 @@ class SettingsViewSet(InstanceMixin, viewsets.GenericViewSet):
553
568
  'last_event': last_event,
554
569
  'weather_forecast': wf_comp_id,
555
570
  'main_alarm_group': main_alarm_group_id,
571
+ 'main_state': main_state,
556
572
  # TODO: Remove these two when the app is updated for everybody.
557
573
  'remote_http': dynamic_settings['core__remote_http'],
558
574
  'local_http': 'https://%s' % get_self_ip(),
simo/core/app_widgets.py CHANGED
@@ -100,3 +100,8 @@ class LockWidget(BaseAppWidget):
100
100
  name = _("Lock")
101
101
  size = [2, 2]
102
102
 
103
+
104
+ class AirQualityWidget(BaseAppWidget):
105
+ uid = 'air-quality'
106
+ name = _("Air Quality")
107
+ size = [2, 2]
simo/core/controllers.py CHANGED
@@ -302,7 +302,7 @@ class ControllerBase(ABC):
302
302
  from .models import Component
303
303
  Component.objects.bulk_send(bulk_send_map)
304
304
 
305
- def history_display(self, values):
305
+ def _history_display(self, values):
306
306
  assert type(values) in (list, tuple)
307
307
 
308
308
  if type(self.component.value) in (int, float):
@@ -445,7 +445,7 @@ class MultiSensor(ControllerBase):
445
445
  ))
446
446
  return value
447
447
 
448
- def history_display(self, values):
448
+ def _history_display(self, values):
449
449
  assert type(values) in (list, tuple)
450
450
 
451
451
  vectors = []
simo/core/events.py CHANGED
@@ -92,7 +92,9 @@ def get_event_obj(payload, model_class=None, gateway=None):
92
92
 
93
93
  class OnChangeMixin:
94
94
 
95
+ _on_change_function = None
95
96
  on_change_fields = ('value', )
97
+ _mqtt_client = None
96
98
 
97
99
  def get_instance(self):
98
100
  # default for component
simo/core/forms.py CHANGED
@@ -212,6 +212,8 @@ class ComponentAdminForm(forms.ModelForm):
212
212
  controller_type = None
213
213
  has_icon = True
214
214
  has_alarm = True
215
+ # do not allow modification via app of these fields
216
+ app_exclude_fields = []
215
217
 
216
218
  # fields that can be edited via SIMO.io app by instance owners.
217
219
  # Users who have is_owner enabled on their user role.
@@ -17,9 +17,6 @@ from simo.core.models import Gateway
17
17
  from simo.core.loggers import get_gw_logger
18
18
 
19
19
 
20
-
21
-
22
-
23
20
  class GatewayRunHandler(multiprocessing.Process):
24
21
  gateway = None
25
22
  logger = None
simo/core/middleware.py CHANGED
@@ -30,12 +30,18 @@ def get_current_instance(request=None):
30
30
  if not instance and request and request.session.get('instance_id'):
31
31
  from simo.core.models import Instance
32
32
  instance = Instance.objects.filter(
33
- id=request.session['instance_id']
33
+ id=request.session['instance_id'], is_active=True
34
34
  ).first()
35
35
  if not instance:
36
36
  del request.session['instance_id']
37
37
  else:
38
38
  introduce_instance(instance, request)
39
+
40
+ if not instance:
41
+ from .models import Instance
42
+ instance = Instance.objects.filter(is_active=True).first()
43
+ if instance:
44
+ introduce_instance(instance)
39
45
  return instance
40
46
 
41
47
 
@@ -0,0 +1,18 @@
1
+ # Generated by Django 4.2.10 on 2024-10-09 09:16
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ('core', '0041_alter_instance_slug'),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AlterField(
14
+ model_name='instance',
15
+ name='timezone',
16
+ field=models.CharField(choices=[('Africa/Abidjan', 'Africa/Abidjan'), ('Africa/Accra', 'Africa/Accra'), ('Africa/Addis_Ababa', 'Africa/Addis_Ababa'), ('Africa/Algiers', 'Africa/Algiers'), ('Africa/Asmara', 'Africa/Asmara'), ('Africa/Asmera', 'Africa/Asmera'), ('Africa/Bamako', 'Africa/Bamako'), ('Africa/Bangui', 'Africa/Bangui'), ('Africa/Banjul', 'Africa/Banjul'), ('Africa/Bissau', 'Africa/Bissau'), ('Africa/Blantyre', 'Africa/Blantyre'), ('Africa/Brazzaville', 'Africa/Brazzaville'), ('Africa/Bujumbura', 'Africa/Bujumbura'), ('Africa/Cairo', 'Africa/Cairo'), ('Africa/Casablanca', 'Africa/Casablanca'), ('Africa/Ceuta', 'Africa/Ceuta'), ('Africa/Conakry', 'Africa/Conakry'), ('Africa/Dakar', 'Africa/Dakar'), ('Africa/Dar_es_Salaam', 'Africa/Dar_es_Salaam'), ('Africa/Djibouti', 'Africa/Djibouti'), ('Africa/Douala', 'Africa/Douala'), ('Africa/El_Aaiun', 'Africa/El_Aaiun'), ('Africa/Freetown', 'Africa/Freetown'), ('Africa/Gaborone', 'Africa/Gaborone'), ('Africa/Harare', 'Africa/Harare'), ('Africa/Johannesburg', 'Africa/Johannesburg'), ('Africa/Juba', 'Africa/Juba'), ('Africa/Kampala', 'Africa/Kampala'), ('Africa/Khartoum', 'Africa/Khartoum'), ('Africa/Kigali', 'Africa/Kigali'), ('Africa/Kinshasa', 'Africa/Kinshasa'), ('Africa/Lagos', 'Africa/Lagos'), ('Africa/Libreville', 'Africa/Libreville'), ('Africa/Lome', 'Africa/Lome'), ('Africa/Luanda', 'Africa/Luanda'), ('Africa/Lubumbashi', 'Africa/Lubumbashi'), ('Africa/Lusaka', 'Africa/Lusaka'), ('Africa/Malabo', 'Africa/Malabo'), ('Africa/Maputo', 'Africa/Maputo'), ('Africa/Maseru', 'Africa/Maseru'), ('Africa/Mbabane', 'Africa/Mbabane'), ('Africa/Mogadishu', 'Africa/Mogadishu'), ('Africa/Monrovia', 'Africa/Monrovia'), ('Africa/Nairobi', 'Africa/Nairobi'), ('Africa/Ndjamena', 'Africa/Ndjamena'), ('Africa/Niamey', 'Africa/Niamey'), ('Africa/Nouakchott', 'Africa/Nouakchott'), ('Africa/Ouagadougou', 'Africa/Ouagadougou'), ('Africa/Porto-Novo', 'Africa/Porto-Novo'), ('Africa/Sao_Tome', 'Africa/Sao_Tome'), ('Africa/Timbuktu', 'Africa/Timbuktu'), ('Africa/Tripoli', 'Africa/Tripoli'), ('Africa/Tunis', 'Africa/Tunis'), ('Africa/Windhoek', 'Africa/Windhoek'), ('America/Adak', 'America/Adak'), ('America/Anchorage', 'America/Anchorage'), ('America/Anguilla', 'America/Anguilla'), ('America/Antigua', 'America/Antigua'), ('America/Araguaina', 'America/Araguaina'), ('America/Argentina/Buenos_Aires', 'America/Argentina/Buenos_Aires'), ('America/Argentina/Catamarca', 'America/Argentina/Catamarca'), ('America/Argentina/ComodRivadavia', 'America/Argentina/ComodRivadavia'), ('America/Argentina/Cordoba', 'America/Argentina/Cordoba'), ('America/Argentina/Jujuy', 'America/Argentina/Jujuy'), ('America/Argentina/La_Rioja', 'America/Argentina/La_Rioja'), ('America/Argentina/Mendoza', 'America/Argentina/Mendoza'), ('America/Argentina/Rio_Gallegos', 'America/Argentina/Rio_Gallegos'), ('America/Argentina/Salta', 'America/Argentina/Salta'), ('America/Argentina/San_Juan', 'America/Argentina/San_Juan'), ('America/Argentina/San_Luis', 'America/Argentina/San_Luis'), ('America/Argentina/Tucuman', 'America/Argentina/Tucuman'), ('America/Argentina/Ushuaia', 'America/Argentina/Ushuaia'), ('America/Aruba', 'America/Aruba'), ('America/Asuncion', 'America/Asuncion'), ('America/Atikokan', 'America/Atikokan'), ('America/Atka', 'America/Atka'), ('America/Bahia', 'America/Bahia'), ('America/Bahia_Banderas', 'America/Bahia_Banderas'), ('America/Barbados', 'America/Barbados'), ('America/Belem', 'America/Belem'), ('America/Belize', 'America/Belize'), ('America/Blanc-Sablon', 'America/Blanc-Sablon'), ('America/Boa_Vista', 'America/Boa_Vista'), ('America/Bogota', 'America/Bogota'), ('America/Boise', 'America/Boise'), ('America/Buenos_Aires', 'America/Buenos_Aires'), ('America/Cambridge_Bay', 'America/Cambridge_Bay'), ('America/Campo_Grande', 'America/Campo_Grande'), ('America/Cancun', 'America/Cancun'), ('America/Caracas', 'America/Caracas'), ('America/Catamarca', 'America/Catamarca'), ('America/Cayenne', 'America/Cayenne'), ('America/Cayman', 'America/Cayman'), ('America/Chicago', 'America/Chicago'), ('America/Chihuahua', 'America/Chihuahua'), ('America/Ciudad_Juarez', 'America/Ciudad_Juarez'), ('America/Coral_Harbour', 'America/Coral_Harbour'), ('America/Cordoba', 'America/Cordoba'), ('America/Costa_Rica', 'America/Costa_Rica'), ('America/Creston', 'America/Creston'), ('America/Cuiaba', 'America/Cuiaba'), ('America/Curacao', 'America/Curacao'), ('America/Danmarkshavn', 'America/Danmarkshavn'), ('America/Dawson', 'America/Dawson'), ('America/Dawson_Creek', 'America/Dawson_Creek'), ('America/Denver', 'America/Denver'), ('America/Detroit', 'America/Detroit'), ('America/Dominica', 'America/Dominica'), ('America/Edmonton', 'America/Edmonton'), ('America/Eirunepe', 'America/Eirunepe'), ('America/El_Salvador', 'America/El_Salvador'), ('America/Ensenada', 'America/Ensenada'), ('America/Fort_Nelson', 'America/Fort_Nelson'), ('America/Fort_Wayne', 'America/Fort_Wayne'), ('America/Fortaleza', 'America/Fortaleza'), ('America/Glace_Bay', 'America/Glace_Bay'), ('America/Godthab', 'America/Godthab'), ('America/Goose_Bay', 'America/Goose_Bay'), ('America/Grand_Turk', 'America/Grand_Turk'), ('America/Grenada', 'America/Grenada'), ('America/Guadeloupe', 'America/Guadeloupe'), ('America/Guatemala', 'America/Guatemala'), ('America/Guayaquil', 'America/Guayaquil'), ('America/Guyana', 'America/Guyana'), ('America/Halifax', 'America/Halifax'), ('America/Havana', 'America/Havana'), ('America/Hermosillo', 'America/Hermosillo'), ('America/Indiana/Indianapolis', 'America/Indiana/Indianapolis'), ('America/Indiana/Knox', 'America/Indiana/Knox'), ('America/Indiana/Marengo', 'America/Indiana/Marengo'), ('America/Indiana/Petersburg', 'America/Indiana/Petersburg'), ('America/Indiana/Tell_City', 'America/Indiana/Tell_City'), ('America/Indiana/Vevay', 'America/Indiana/Vevay'), ('America/Indiana/Vincennes', 'America/Indiana/Vincennes'), ('America/Indiana/Winamac', 'America/Indiana/Winamac'), ('America/Indianapolis', 'America/Indianapolis'), ('America/Inuvik', 'America/Inuvik'), ('America/Iqaluit', 'America/Iqaluit'), ('America/Jamaica', 'America/Jamaica'), ('America/Jujuy', 'America/Jujuy'), ('America/Juneau', 'America/Juneau'), ('America/Kentucky/Louisville', 'America/Kentucky/Louisville'), ('America/Kentucky/Monticello', 'America/Kentucky/Monticello'), ('America/Knox_IN', 'America/Knox_IN'), ('America/Kralendijk', 'America/Kralendijk'), ('America/La_Paz', 'America/La_Paz'), ('America/Lima', 'America/Lima'), ('America/Los_Angeles', 'America/Los_Angeles'), ('America/Louisville', 'America/Louisville'), ('America/Lower_Princes', 'America/Lower_Princes'), ('America/Maceio', 'America/Maceio'), ('America/Managua', 'America/Managua'), ('America/Manaus', 'America/Manaus'), ('America/Marigot', 'America/Marigot'), ('America/Martinique', 'America/Martinique'), ('America/Matamoros', 'America/Matamoros'), ('America/Mazatlan', 'America/Mazatlan'), ('America/Mendoza', 'America/Mendoza'), ('America/Menominee', 'America/Menominee'), ('America/Merida', 'America/Merida'), ('America/Metlakatla', 'America/Metlakatla'), ('America/Mexico_City', 'America/Mexico_City'), ('America/Miquelon', 'America/Miquelon'), ('America/Moncton', 'America/Moncton'), ('America/Monterrey', 'America/Monterrey'), ('America/Montevideo', 'America/Montevideo'), ('America/Montreal', 'America/Montreal'), ('America/Montserrat', 'America/Montserrat'), ('America/Nassau', 'America/Nassau'), ('America/New_York', 'America/New_York'), ('America/Nipigon', 'America/Nipigon'), ('America/Nome', 'America/Nome'), ('America/Noronha', 'America/Noronha'), ('America/North_Dakota/Beulah', 'America/North_Dakota/Beulah'), ('America/North_Dakota/Center', 'America/North_Dakota/Center'), ('America/North_Dakota/New_Salem', 'America/North_Dakota/New_Salem'), ('America/Nuuk', 'America/Nuuk'), ('America/Ojinaga', 'America/Ojinaga'), ('America/Panama', 'America/Panama'), ('America/Pangnirtung', 'America/Pangnirtung'), ('America/Paramaribo', 'America/Paramaribo'), ('America/Phoenix', 'America/Phoenix'), ('America/Port-au-Prince', 'America/Port-au-Prince'), ('America/Port_of_Spain', 'America/Port_of_Spain'), ('America/Porto_Acre', 'America/Porto_Acre'), ('America/Porto_Velho', 'America/Porto_Velho'), ('America/Puerto_Rico', 'America/Puerto_Rico'), ('America/Punta_Arenas', 'America/Punta_Arenas'), ('America/Rainy_River', 'America/Rainy_River'), ('America/Rankin_Inlet', 'America/Rankin_Inlet'), ('America/Recife', 'America/Recife'), ('America/Regina', 'America/Regina'), ('America/Resolute', 'America/Resolute'), ('America/Rio_Branco', 'America/Rio_Branco'), ('America/Rosario', 'America/Rosario'), ('America/Santa_Isabel', 'America/Santa_Isabel'), ('America/Santarem', 'America/Santarem'), ('America/Santiago', 'America/Santiago'), ('America/Santo_Domingo', 'America/Santo_Domingo'), ('America/Sao_Paulo', 'America/Sao_Paulo'), ('America/Scoresbysund', 'America/Scoresbysund'), ('America/Shiprock', 'America/Shiprock'), ('America/Sitka', 'America/Sitka'), ('America/St_Barthelemy', 'America/St_Barthelemy'), ('America/St_Johns', 'America/St_Johns'), ('America/St_Kitts', 'America/St_Kitts'), ('America/St_Lucia', 'America/St_Lucia'), ('America/St_Thomas', 'America/St_Thomas'), ('America/St_Vincent', 'America/St_Vincent'), ('America/Swift_Current', 'America/Swift_Current'), ('America/Tegucigalpa', 'America/Tegucigalpa'), ('America/Thule', 'America/Thule'), ('America/Thunder_Bay', 'America/Thunder_Bay'), ('America/Tijuana', 'America/Tijuana'), ('America/Toronto', 'America/Toronto'), ('America/Tortola', 'America/Tortola'), ('America/Vancouver', 'America/Vancouver'), ('America/Virgin', 'America/Virgin'), ('America/Whitehorse', 'America/Whitehorse'), ('America/Winnipeg', 'America/Winnipeg'), ('America/Yakutat', 'America/Yakutat'), ('America/Yellowknife', 'America/Yellowknife'), ('Antarctica/Casey', 'Antarctica/Casey'), ('Antarctica/Davis', 'Antarctica/Davis'), ('Antarctica/DumontDUrville', 'Antarctica/DumontDUrville'), ('Antarctica/Macquarie', 'Antarctica/Macquarie'), ('Antarctica/Mawson', 'Antarctica/Mawson'), ('Antarctica/McMurdo', 'Antarctica/McMurdo'), ('Antarctica/Palmer', 'Antarctica/Palmer'), ('Antarctica/Rothera', 'Antarctica/Rothera'), ('Antarctica/South_Pole', 'Antarctica/South_Pole'), ('Antarctica/Syowa', 'Antarctica/Syowa'), ('Antarctica/Troll', 'Antarctica/Troll'), ('Antarctica/Vostok', 'Antarctica/Vostok'), ('Arctic/Longyearbyen', 'Arctic/Longyearbyen'), ('Asia/Aden', 'Asia/Aden'), ('Asia/Almaty', 'Asia/Almaty'), ('Asia/Amman', 'Asia/Amman'), ('Asia/Anadyr', 'Asia/Anadyr'), ('Asia/Aqtau', 'Asia/Aqtau'), ('Asia/Aqtobe', 'Asia/Aqtobe'), ('Asia/Ashgabat', 'Asia/Ashgabat'), ('Asia/Ashkhabad', 'Asia/Ashkhabad'), ('Asia/Atyrau', 'Asia/Atyrau'), ('Asia/Baghdad', 'Asia/Baghdad'), ('Asia/Bahrain', 'Asia/Bahrain'), ('Asia/Baku', 'Asia/Baku'), ('Asia/Bangkok', 'Asia/Bangkok'), ('Asia/Barnaul', 'Asia/Barnaul'), ('Asia/Beirut', 'Asia/Beirut'), ('Asia/Bishkek', 'Asia/Bishkek'), ('Asia/Brunei', 'Asia/Brunei'), ('Asia/Calcutta', 'Asia/Calcutta'), ('Asia/Chita', 'Asia/Chita'), ('Asia/Choibalsan', 'Asia/Choibalsan'), ('Asia/Chongqing', 'Asia/Chongqing'), ('Asia/Chungking', 'Asia/Chungking'), ('Asia/Colombo', 'Asia/Colombo'), ('Asia/Dacca', 'Asia/Dacca'), ('Asia/Damascus', 'Asia/Damascus'), ('Asia/Dhaka', 'Asia/Dhaka'), ('Asia/Dili', 'Asia/Dili'), ('Asia/Dubai', 'Asia/Dubai'), ('Asia/Dushanbe', 'Asia/Dushanbe'), ('Asia/Famagusta', 'Asia/Famagusta'), ('Asia/Gaza', 'Asia/Gaza'), ('Asia/Harbin', 'Asia/Harbin'), ('Asia/Hebron', 'Asia/Hebron'), ('Asia/Ho_Chi_Minh', 'Asia/Ho_Chi_Minh'), ('Asia/Hong_Kong', 'Asia/Hong_Kong'), ('Asia/Hovd', 'Asia/Hovd'), ('Asia/Irkutsk', 'Asia/Irkutsk'), ('Asia/Istanbul', 'Asia/Istanbul'), ('Asia/Jakarta', 'Asia/Jakarta'), ('Asia/Jayapura', 'Asia/Jayapura'), ('Asia/Jerusalem', 'Asia/Jerusalem'), ('Asia/Kabul', 'Asia/Kabul'), ('Asia/Kamchatka', 'Asia/Kamchatka'), ('Asia/Karachi', 'Asia/Karachi'), ('Asia/Kashgar', 'Asia/Kashgar'), ('Asia/Kathmandu', 'Asia/Kathmandu'), ('Asia/Katmandu', 'Asia/Katmandu'), ('Asia/Khandyga', 'Asia/Khandyga'), ('Asia/Kolkata', 'Asia/Kolkata'), ('Asia/Krasnoyarsk', 'Asia/Krasnoyarsk'), ('Asia/Kuala_Lumpur', 'Asia/Kuala_Lumpur'), ('Asia/Kuching', 'Asia/Kuching'), ('Asia/Kuwait', 'Asia/Kuwait'), ('Asia/Macao', 'Asia/Macao'), ('Asia/Macau', 'Asia/Macau'), ('Asia/Magadan', 'Asia/Magadan'), ('Asia/Makassar', 'Asia/Makassar'), ('Asia/Manila', 'Asia/Manila'), ('Asia/Muscat', 'Asia/Muscat'), ('Asia/Nicosia', 'Asia/Nicosia'), ('Asia/Novokuznetsk', 'Asia/Novokuznetsk'), ('Asia/Novosibirsk', 'Asia/Novosibirsk'), ('Asia/Omsk', 'Asia/Omsk'), ('Asia/Oral', 'Asia/Oral'), ('Asia/Phnom_Penh', 'Asia/Phnom_Penh'), ('Asia/Pontianak', 'Asia/Pontianak'), ('Asia/Pyongyang', 'Asia/Pyongyang'), ('Asia/Qatar', 'Asia/Qatar'), ('Asia/Qostanay', 'Asia/Qostanay'), ('Asia/Qyzylorda', 'Asia/Qyzylorda'), ('Asia/Rangoon', 'Asia/Rangoon'), ('Asia/Riyadh', 'Asia/Riyadh'), ('Asia/Saigon', 'Asia/Saigon'), ('Asia/Sakhalin', 'Asia/Sakhalin'), ('Asia/Samarkand', 'Asia/Samarkand'), ('Asia/Seoul', 'Asia/Seoul'), ('Asia/Shanghai', 'Asia/Shanghai'), ('Asia/Singapore', 'Asia/Singapore'), ('Asia/Srednekolymsk', 'Asia/Srednekolymsk'), ('Asia/Taipei', 'Asia/Taipei'), ('Asia/Tashkent', 'Asia/Tashkent'), ('Asia/Tbilisi', 'Asia/Tbilisi'), ('Asia/Tehran', 'Asia/Tehran'), ('Asia/Tel_Aviv', 'Asia/Tel_Aviv'), ('Asia/Thimbu', 'Asia/Thimbu'), ('Asia/Thimphu', 'Asia/Thimphu'), ('Asia/Tokyo', 'Asia/Tokyo'), ('Asia/Tomsk', 'Asia/Tomsk'), ('Asia/Ujung_Pandang', 'Asia/Ujung_Pandang'), ('Asia/Ulaanbaatar', 'Asia/Ulaanbaatar'), ('Asia/Ulan_Bator', 'Asia/Ulan_Bator'), ('Asia/Urumqi', 'Asia/Urumqi'), ('Asia/Ust-Nera', 'Asia/Ust-Nera'), ('Asia/Vientiane', 'Asia/Vientiane'), ('Asia/Vladivostok', 'Asia/Vladivostok'), ('Asia/Yakutsk', 'Asia/Yakutsk'), ('Asia/Yangon', 'Asia/Yangon'), ('Asia/Yekaterinburg', 'Asia/Yekaterinburg'), ('Asia/Yerevan', 'Asia/Yerevan'), ('Atlantic/Azores', 'Atlantic/Azores'), ('Atlantic/Bermuda', 'Atlantic/Bermuda'), ('Atlantic/Canary', 'Atlantic/Canary'), ('Atlantic/Cape_Verde', 'Atlantic/Cape_Verde'), ('Atlantic/Faeroe', 'Atlantic/Faeroe'), ('Atlantic/Faroe', 'Atlantic/Faroe'), ('Atlantic/Jan_Mayen', 'Atlantic/Jan_Mayen'), ('Atlantic/Madeira', 'Atlantic/Madeira'), ('Atlantic/Reykjavik', 'Atlantic/Reykjavik'), ('Atlantic/South_Georgia', 'Atlantic/South_Georgia'), ('Atlantic/St_Helena', 'Atlantic/St_Helena'), ('Atlantic/Stanley', 'Atlantic/Stanley'), ('Australia/ACT', 'Australia/ACT'), ('Australia/Adelaide', 'Australia/Adelaide'), ('Australia/Brisbane', 'Australia/Brisbane'), ('Australia/Broken_Hill', 'Australia/Broken_Hill'), ('Australia/Canberra', 'Australia/Canberra'), ('Australia/Currie', 'Australia/Currie'), ('Australia/Darwin', 'Australia/Darwin'), ('Australia/Eucla', 'Australia/Eucla'), ('Australia/Hobart', 'Australia/Hobart'), ('Australia/LHI', 'Australia/LHI'), ('Australia/Lindeman', 'Australia/Lindeman'), ('Australia/Lord_Howe', 'Australia/Lord_Howe'), ('Australia/Melbourne', 'Australia/Melbourne'), ('Australia/NSW', 'Australia/NSW'), ('Australia/North', 'Australia/North'), ('Australia/Perth', 'Australia/Perth'), ('Australia/Queensland', 'Australia/Queensland'), ('Australia/South', 'Australia/South'), ('Australia/Sydney', 'Australia/Sydney'), ('Australia/Tasmania', 'Australia/Tasmania'), ('Australia/Victoria', 'Australia/Victoria'), ('Australia/West', 'Australia/West'), ('Australia/Yancowinna', 'Australia/Yancowinna'), ('Brazil/Acre', 'Brazil/Acre'), ('Brazil/DeNoronha', 'Brazil/DeNoronha'), ('Brazil/East', 'Brazil/East'), ('Brazil/West', 'Brazil/West'), ('CET', 'CET'), ('CST6CDT', 'CST6CDT'), ('Canada/Atlantic', 'Canada/Atlantic'), ('Canada/Central', 'Canada/Central'), ('Canada/Eastern', 'Canada/Eastern'), ('Canada/Mountain', 'Canada/Mountain'), ('Canada/Newfoundland', 'Canada/Newfoundland'), ('Canada/Pacific', 'Canada/Pacific'), ('Canada/Saskatchewan', 'Canada/Saskatchewan'), ('Canada/Yukon', 'Canada/Yukon'), ('Chile/Continental', 'Chile/Continental'), ('Chile/EasterIsland', 'Chile/EasterIsland'), ('Cuba', 'Cuba'), ('EET', 'EET'), ('EST', 'EST'), ('EST5EDT', 'EST5EDT'), ('Egypt', 'Egypt'), ('Eire', 'Eire'), ('Etc/GMT', 'Etc/GMT'), ('Etc/GMT+0', 'Etc/GMT+0'), ('Etc/GMT+1', 'Etc/GMT+1'), ('Etc/GMT+10', 'Etc/GMT+10'), ('Etc/GMT+11', 'Etc/GMT+11'), ('Etc/GMT+12', 'Etc/GMT+12'), ('Etc/GMT+2', 'Etc/GMT+2'), ('Etc/GMT+3', 'Etc/GMT+3'), ('Etc/GMT+4', 'Etc/GMT+4'), ('Etc/GMT+5', 'Etc/GMT+5'), ('Etc/GMT+6', 'Etc/GMT+6'), ('Etc/GMT+7', 'Etc/GMT+7'), ('Etc/GMT+8', 'Etc/GMT+8'), ('Etc/GMT+9', 'Etc/GMT+9'), ('Etc/GMT-0', 'Etc/GMT-0'), ('Etc/GMT-1', 'Etc/GMT-1'), ('Etc/GMT-10', 'Etc/GMT-10'), ('Etc/GMT-11', 'Etc/GMT-11'), ('Etc/GMT-12', 'Etc/GMT-12'), ('Etc/GMT-13', 'Etc/GMT-13'), ('Etc/GMT-14', 'Etc/GMT-14'), ('Etc/GMT-2', 'Etc/GMT-2'), ('Etc/GMT-3', 'Etc/GMT-3'), ('Etc/GMT-4', 'Etc/GMT-4'), ('Etc/GMT-5', 'Etc/GMT-5'), ('Etc/GMT-6', 'Etc/GMT-6'), ('Etc/GMT-7', 'Etc/GMT-7'), ('Etc/GMT-8', 'Etc/GMT-8'), ('Etc/GMT-9', 'Etc/GMT-9'), ('Etc/GMT0', 'Etc/GMT0'), ('Etc/Greenwich', 'Etc/Greenwich'), ('Etc/UCT', 'Etc/UCT'), ('Etc/UTC', 'Etc/UTC'), ('Etc/Universal', 'Etc/Universal'), ('Etc/Zulu', 'Etc/Zulu'), ('Europe/Amsterdam', 'Europe/Amsterdam'), ('Europe/Andorra', 'Europe/Andorra'), ('Europe/Astrakhan', 'Europe/Astrakhan'), ('Europe/Athens', 'Europe/Athens'), ('Europe/Belfast', 'Europe/Belfast'), ('Europe/Belgrade', 'Europe/Belgrade'), ('Europe/Berlin', 'Europe/Berlin'), ('Europe/Bratislava', 'Europe/Bratislava'), ('Europe/Brussels', 'Europe/Brussels'), ('Europe/Bucharest', 'Europe/Bucharest'), ('Europe/Budapest', 'Europe/Budapest'), ('Europe/Busingen', 'Europe/Busingen'), ('Europe/Chisinau', 'Europe/Chisinau'), ('Europe/Copenhagen', 'Europe/Copenhagen'), ('Europe/Dublin', 'Europe/Dublin'), ('Europe/Gibraltar', 'Europe/Gibraltar'), ('Europe/Guernsey', 'Europe/Guernsey'), ('Europe/Helsinki', 'Europe/Helsinki'), ('Europe/Isle_of_Man', 'Europe/Isle_of_Man'), ('Europe/Istanbul', 'Europe/Istanbul'), ('Europe/Jersey', 'Europe/Jersey'), ('Europe/Kaliningrad', 'Europe/Kaliningrad'), ('Europe/Kiev', 'Europe/Kiev'), ('Europe/Kirov', 'Europe/Kirov'), ('Europe/Kyiv', 'Europe/Kyiv'), ('Europe/Lisbon', 'Europe/Lisbon'), ('Europe/Ljubljana', 'Europe/Ljubljana'), ('Europe/London', 'Europe/London'), ('Europe/Luxembourg', 'Europe/Luxembourg'), ('Europe/Madrid', 'Europe/Madrid'), ('Europe/Malta', 'Europe/Malta'), ('Europe/Mariehamn', 'Europe/Mariehamn'), ('Europe/Minsk', 'Europe/Minsk'), ('Europe/Monaco', 'Europe/Monaco'), ('Europe/Moscow', 'Europe/Moscow'), ('Europe/Nicosia', 'Europe/Nicosia'), ('Europe/Oslo', 'Europe/Oslo'), ('Europe/Paris', 'Europe/Paris'), ('Europe/Podgorica', 'Europe/Podgorica'), ('Europe/Prague', 'Europe/Prague'), ('Europe/Riga', 'Europe/Riga'), ('Europe/Rome', 'Europe/Rome'), ('Europe/Samara', 'Europe/Samara'), ('Europe/San_Marino', 'Europe/San_Marino'), ('Europe/Sarajevo', 'Europe/Sarajevo'), ('Europe/Saratov', 'Europe/Saratov'), ('Europe/Simferopol', 'Europe/Simferopol'), ('Europe/Skopje', 'Europe/Skopje'), ('Europe/Sofia', 'Europe/Sofia'), ('Europe/Stockholm', 'Europe/Stockholm'), ('Europe/Tallinn', 'Europe/Tallinn'), ('Europe/Tirane', 'Europe/Tirane'), ('Europe/Tiraspol', 'Europe/Tiraspol'), ('Europe/Ulyanovsk', 'Europe/Ulyanovsk'), ('Europe/Uzhgorod', 'Europe/Uzhgorod'), ('Europe/Vaduz', 'Europe/Vaduz'), ('Europe/Vatican', 'Europe/Vatican'), ('Europe/Vienna', 'Europe/Vienna'), ('Europe/Vilnius', 'Europe/Vilnius'), ('Europe/Volgograd', 'Europe/Volgograd'), ('Europe/Warsaw', 'Europe/Warsaw'), ('Europe/Zagreb', 'Europe/Zagreb'), ('Europe/Zaporozhye', 'Europe/Zaporozhye'), ('Europe/Zurich', 'Europe/Zurich'), ('GB', 'GB'), ('GB-Eire', 'GB-Eire'), ('GMT', 'GMT'), ('GMT+0', 'GMT+0'), ('GMT-0', 'GMT-0'), ('GMT0', 'GMT0'), ('Greenwich', 'Greenwich'), ('HST', 'HST'), ('Hongkong', 'Hongkong'), ('Iceland', 'Iceland'), ('Indian/Antananarivo', 'Indian/Antananarivo'), ('Indian/Chagos', 'Indian/Chagos'), ('Indian/Christmas', 'Indian/Christmas'), ('Indian/Cocos', 'Indian/Cocos'), ('Indian/Comoro', 'Indian/Comoro'), ('Indian/Kerguelen', 'Indian/Kerguelen'), ('Indian/Mahe', 'Indian/Mahe'), ('Indian/Maldives', 'Indian/Maldives'), ('Indian/Mauritius', 'Indian/Mauritius'), ('Indian/Mayotte', 'Indian/Mayotte'), ('Indian/Reunion', 'Indian/Reunion'), ('Iran', 'Iran'), ('Israel', 'Israel'), ('Jamaica', 'Jamaica'), ('Japan', 'Japan'), ('Kwajalein', 'Kwajalein'), ('Libya', 'Libya'), ('MET', 'MET'), ('MST', 'MST'), ('MST7MDT', 'MST7MDT'), ('Mexico/BajaNorte', 'Mexico/BajaNorte'), ('Mexico/BajaSur', 'Mexico/BajaSur'), ('Mexico/General', 'Mexico/General'), ('NZ', 'NZ'), ('NZ-CHAT', 'NZ-CHAT'), ('Navajo', 'Navajo'), ('PRC', 'PRC'), ('PST8PDT', 'PST8PDT'), ('Pacific/Apia', 'Pacific/Apia'), ('Pacific/Auckland', 'Pacific/Auckland'), ('Pacific/Bougainville', 'Pacific/Bougainville'), ('Pacific/Chatham', 'Pacific/Chatham'), ('Pacific/Chuuk', 'Pacific/Chuuk'), ('Pacific/Easter', 'Pacific/Easter'), ('Pacific/Efate', 'Pacific/Efate'), ('Pacific/Enderbury', 'Pacific/Enderbury'), ('Pacific/Fakaofo', 'Pacific/Fakaofo'), ('Pacific/Fiji', 'Pacific/Fiji'), ('Pacific/Funafuti', 'Pacific/Funafuti'), ('Pacific/Galapagos', 'Pacific/Galapagos'), ('Pacific/Gambier', 'Pacific/Gambier'), ('Pacific/Guadalcanal', 'Pacific/Guadalcanal'), ('Pacific/Guam', 'Pacific/Guam'), ('Pacific/Honolulu', 'Pacific/Honolulu'), ('Pacific/Johnston', 'Pacific/Johnston'), ('Pacific/Kanton', 'Pacific/Kanton'), ('Pacific/Kiritimati', 'Pacific/Kiritimati'), ('Pacific/Kosrae', 'Pacific/Kosrae'), ('Pacific/Kwajalein', 'Pacific/Kwajalein'), ('Pacific/Majuro', 'Pacific/Majuro'), ('Pacific/Marquesas', 'Pacific/Marquesas'), ('Pacific/Midway', 'Pacific/Midway'), ('Pacific/Nauru', 'Pacific/Nauru'), ('Pacific/Niue', 'Pacific/Niue'), ('Pacific/Norfolk', 'Pacific/Norfolk'), ('Pacific/Noumea', 'Pacific/Noumea'), ('Pacific/Pago_Pago', 'Pacific/Pago_Pago'), ('Pacific/Palau', 'Pacific/Palau'), ('Pacific/Pitcairn', 'Pacific/Pitcairn'), ('Pacific/Pohnpei', 'Pacific/Pohnpei'), ('Pacific/Ponape', 'Pacific/Ponape'), ('Pacific/Port_Moresby', 'Pacific/Port_Moresby'), ('Pacific/Rarotonga', 'Pacific/Rarotonga'), ('Pacific/Saipan', 'Pacific/Saipan'), ('Pacific/Samoa', 'Pacific/Samoa'), ('Pacific/Tahiti', 'Pacific/Tahiti'), ('Pacific/Tarawa', 'Pacific/Tarawa'), ('Pacific/Tongatapu', 'Pacific/Tongatapu'), ('Pacific/Truk', 'Pacific/Truk'), ('Pacific/Wake', 'Pacific/Wake'), ('Pacific/Wallis', 'Pacific/Wallis'), ('Pacific/Yap', 'Pacific/Yap'), ('Poland', 'Poland'), ('Portugal', 'Portugal'), ('ROC', 'ROC'), ('ROK', 'ROK'), ('Singapore', 'Singapore'), ('Turkey', 'Turkey'), ('UCT', 'UCT'), ('US/Alaska', 'US/Alaska'), ('US/Aleutian', 'US/Aleutian'), ('US/Arizona', 'US/Arizona'), ('US/Central', 'US/Central'), ('US/East-Indiana', 'US/East-Indiana'), ('US/Eastern', 'US/Eastern'), ('US/Hawaii', 'US/Hawaii'), ('US/Indiana-Starke', 'US/Indiana-Starke'), ('US/Michigan', 'US/Michigan'), ('US/Mountain', 'US/Mountain'), ('US/Pacific', 'US/Pacific'), ('US/Samoa', 'US/Samoa'), ('UTC', 'UTC'), ('Universal', 'Universal'), ('W-SU', 'W-SU'), ('WET', 'WET'), ('Zulu', 'Zulu')], db_index=True, max_length=50),
17
+ ),
18
+ ]
simo/core/models.py CHANGED
@@ -406,8 +406,8 @@ def is_in_alarm(self):
406
406
 
407
407
  _controller_initiated = False
408
408
 
409
- _mqtt_client = None
410
- _on_change_function = None
409
+
410
+
411
411
  _obj_ct_id = 0
412
412
 
413
413
  class Meta:
@@ -517,11 +517,13 @@ def is_in_alarm(self):
517
517
  actor.last_action = timezone.now()
518
518
  actor.save()
519
519
 
520
- if any(
521
- f in dirty_fields for f in
522
- ['value', 'arm_status', 'battery_level', 'alive', 'meta']
523
- ):
520
+ changing_fields = ['value', 'arm_status', 'battery_level', 'alive', 'meta']
521
+ if any(f in dirty_fields for f in changing_fields):
524
522
  self.last_change = timezone.now()
523
+ if 'update_fields' in kwargs \
524
+ and 'last_change' not in kwargs['update_fields']:
525
+ kwargs['update_fields'].append('last_change')
526
+
525
527
 
526
528
  modifying_fields = (
527
529
  'name', 'icon', 'zone', 'category', 'config', 'meta',
@@ -529,6 +531,9 @@ def is_in_alarm(self):
529
531
  )
530
532
  if any(f in dirty_fields for f in modifying_fields):
531
533
  self.last_modified = timezone.now()
534
+ if 'update_fields' in kwargs \
535
+ and 'last_modified' not in kwargs['update_fields']:
536
+ kwargs['update_fields'].append('last_modified')
532
537
 
533
538
  obj = super().save(*args, **kwargs)
534
539
 
@@ -581,6 +586,21 @@ def is_in_alarm(self):
581
586
  return False
582
587
  return perm.write
583
588
 
589
+ def get_controller_methods(self):
590
+ c_methods = []
591
+ for m in inspect.getmembers(
592
+ self.controller, predicate=inspect.ismethod
593
+ ):
594
+ method = m[0]
595
+ if method.startswith('_'):
596
+ continue
597
+ if method in ('info', 'set'):
598
+ continue
599
+ c_methods.append(method)
600
+ if self.alarm_category:
601
+ c_methods.extend(['arm', 'disarm'])
602
+ return c_methods
603
+
584
604
 
585
605
  class ComponentHistory(models.Model):
586
606
  component = models.ForeignKey(
simo/core/serializers.py CHANGED
@@ -27,6 +27,8 @@ from .forms import ComponentAdminForm
27
27
  from .models import Category, Zone, Icon, ComponentHistory
28
28
 
29
29
 
30
+
31
+
30
32
  class TimestampField(serializers.Field):
31
33
 
32
34
  def to_representation(self, value):
@@ -318,7 +320,7 @@ class ComponentSerializer(FormSerializer):
318
320
  form_field = form[field_name]
319
321
 
320
322
  cls = form_field.field.__class__
321
- if field_name == 'notes':
323
+ if isinstance(form_field.field.widget, forms.Textarea):
322
324
  serializer_field_class = TextAreaSerializerField
323
325
  else:
324
326
  try:
@@ -416,16 +418,19 @@ class ComponentSerializer(FormSerializer):
416
418
  controller_uid=controller_uid, instance=instance,
417
419
  **kwargs
418
420
  )
419
- # only masters and superusers can fully manage components via app
420
- # others can only change basic fields
421
- if not self.context['request'].user.is_master:
422
- user_role = self.context['request'].user.get_role(
423
- self.context['instance']
424
- )
425
- if not user_role.is_superuser:
426
- for field_name in list(form.fields.keys()):
427
- if field_name not in form.basic_fields:
428
- del form.fields[field_name]
421
+
422
+ user_role = self.context['request'].user.get_role(
423
+ self.context['instance']
424
+ )
425
+ for field_name in list(form.fields.keys()):
426
+ if field_name in form.app_exclude_fields:
427
+ del form.fields[field_name]
428
+ continue
429
+ if field_name in form.basic_fields:
430
+ continue
431
+ if self.context['request'].user.is_master or user_role.is_superuser:
432
+ continue
433
+ del form.fields[field_name]
429
434
 
430
435
  if form_key is not None:
431
436
  self.context['forms'][form_key] = form
@@ -492,12 +497,7 @@ class ComponentSerializer(FormSerializer):
492
497
  raise serializers.ValidationError(form.errors)
493
498
 
494
499
  def get_controller_methods(self, obj):
495
- c_methods = [m[0] for m in inspect.getmembers(
496
- obj.controller, predicate=inspect.ismethod
497
- ) if not m[0].startswith('_')]
498
- if obj.alarm_category:
499
- c_methods.extend(['arm', 'disarm'])
500
- return c_methods
500
+ return obj.get_controller_methods()
501
501
 
502
502
  def get_info(self, obj):
503
503
  if obj.controller:
simo/core/tasks.py CHANGED
@@ -211,7 +211,11 @@ def sync_with_remote():
211
211
  ).first()
212
212
  if weather_component:
213
213
  weather_component.track_history = False
214
- weather_component.controller.set(weather_forecast)
214
+ weather_component.controller.set(
215
+ weather_forecast.pop('current', None)
216
+ )
217
+ weather_component.meta['forecast'] = weather_forecast
218
+ weather_component.save()
215
219
 
216
220
  for email, options in users_data.items():
217
221
 
@@ -380,15 +384,14 @@ def low_battery_notifications():
380
384
  zone__instance=instance,
381
385
  battery_level__isnull=False, battery_level__lt=20
382
386
  ):
383
- users = User.objects.filter(
384
- roles__is_owner=True, roles__instance=comp.zone.instance,
385
- instance_roles__is_active=True
386
- ).distinct()
387
- if users:
387
+ iusers = comp.zone.instance.instance_users.filter(
388
+ is_active=True, role__is_owner=True
389
+ )
390
+ if iusers:
388
391
  notify_users(
389
392
  comp.zone.instance, 'warning',
390
393
  f"Low battery ({comp.battery_level}%) on {comp}",
391
- component=comp, users=users
394
+ component=comp, instance_users=iusers
392
395
  )
393
396
 
394
397
 
Binary file
simo/fleet/controllers.py CHANGED
@@ -10,8 +10,7 @@ from simo.core.controllers import (
10
10
  Switch as BaseSwitch, Dimmer as BaseDimmer,
11
11
  MultiSensor as BaseMultiSensor, RGBWLight as BaseRGBWLight
12
12
  )
13
- from simo.conf import dynamic_settings
14
- from simo.core.app_widgets import NumericSensorWidget
13
+ from simo.core.app_widgets import NumericSensorWidget, AirQualityWidget
15
14
  from simo.core.controllers import Lock, ControllerBase, SingleSwitchWidget
16
15
  from simo.core.utils.helpers import heat_index
17
16
  from simo.core.utils.serialization import (
@@ -23,7 +22,7 @@ from .gateways import FleetGatewayHandler
23
22
  from .forms import (
24
23
  ColonelPinChoiceField,
25
24
  ColonelBinarySensorConfigForm, ColonelButtonConfigForm,
26
- ColonelSwitchConfigForm, ColonelPWMOutputConfigForm,
25
+ ColonelSwitchConfigForm, ColonelPWMOutputConfigForm, DCDriverConfigForm,
27
26
  ColonelNumericSensorConfigForm, ColonelRGBLightConfigForm,
28
27
  ColonelDHTSensorConfigForm, DS18B20SensorConfigForm,
29
28
  BME680SensorConfigForm, MPC9808SensorConfigForm, ENS160SensorConfigForm,
@@ -239,6 +238,7 @@ class ENS160AirQualitySensor(FleeDeviceMixin, BaseMultiSensor):
239
238
  gateway_class = FleetGatewayHandler
240
239
  config_form = ENS160SensorConfigForm
241
240
  name = "ENS160 Air Quality Sensor (I2C)"
241
+ app_widget = AirQualityWidget
242
242
 
243
243
  default_value = [
244
244
  ["CO2", 0, "ppm"],
@@ -386,42 +386,87 @@ class PWMOutput(FadeMixin, FleeDeviceMixin, BasicOutputMixin, BaseDimmer):
386
386
  value = conf.get('min', 0)
387
387
 
388
388
  if value >= conf.get('max', 100):
389
- if conf.get('inverse'):
390
- pwm_value = 0
391
- else:
392
- pwm_value = 1023
389
+ pwm_value = 0
393
390
  elif value <= conf.get('min', 100):
394
- if conf.get('inverse'):
395
- pwm_value = 1023
396
- else:
397
- pwm_value = 0
391
+ pwm_value = 1023
398
392
  else:
399
393
  val_amplitude = conf.get('max', 100) - conf.get('min', 0)
400
394
  val_relative = value / val_amplitude
401
- pwm_amplitude = conf.get('duty_max', 1023) - conf.get('duty_min', 0.0)
402
- pwm_value = conf.get('duty_min', 0.0) + pwm_amplitude * val_relative
403
395
 
404
- if conf.get('inverse'):
405
- pwm_value = conf.get('duty_max', 1023) - pwm_value + conf.get('duty_min')
396
+ duty_max = 1023 - (conf.get('device_min', 0) * 0.01 * 1023)
397
+ duty_min = 1023 - conf.get('device_max', 100) * 0.01 * 1023
398
+
399
+ pwm_amplitude = duty_max - duty_min
400
+ pwm_value = duty_min + pwm_amplitude * val_relative
401
+
402
+ pwm_value = duty_max - pwm_value + duty_min
406
403
 
407
404
  return pwm_value
408
405
 
409
406
  def _prepare_for_set(self, pwm_value):
410
407
  conf = self.component.config
411
- if pwm_value > conf.get('duty_max', 1023):
408
+ duty_max = 1023 - (conf.get('device_min', 0) * 0.01 * 1023)
409
+ duty_min = 1023 - conf.get('device_max', 100) * 0.01 * 1023
410
+
411
+ if pwm_value > duty_max:
412
412
  value = conf.get('max', 100)
413
- elif pwm_value < conf.get('duty_min', 0.0):
413
+ elif pwm_value < duty_min:
414
414
  value = conf.get('min', 0)
415
415
  else:
416
- pwm_amplitude = conf.get('duty_max', 1023) - conf.get('duty_min', 0.0)
417
- relative_value = (pwm_value - conf.get('duty_min', 0.0)) / pwm_amplitude
416
+ pwm_amplitude =duty_max - duty_min
417
+ relative_value = (pwm_value - duty_min) / pwm_amplitude
418
418
  val_amplitude = conf.get('max', 100) - conf.get('min', 0)
419
419
  value = conf.get('min', 0) + val_amplitude * relative_value
420
420
 
421
- if self.component.config.get('inverse'):
422
- value = conf.get('max', 100) - value + conf.get('min', 0)
421
+ value = conf.get('max', 100) - value + conf.get('min', 0)
423
422
 
424
- return value
423
+ return round(value, 3)
424
+
425
+
426
+ class DCDriver(FadeMixin, FleeDeviceMixin, BasicOutputMixin, BaseDimmer):
427
+ name = "0 - 24V DC Driver"
428
+ config_form = DCDriverConfigForm
429
+ default_value_units = 'V'
430
+
431
+ def _prepare_for_send(self, value):
432
+ conf = self.component.config
433
+ if value >= conf.get('max', 24):
434
+ value = conf.get('max', 24)
435
+ elif value < conf.get('min', 0):
436
+ value = conf.get('min', 0)
437
+
438
+ if value >= conf.get('max', 24):
439
+ pwm_value = 1023
440
+ elif value <= conf.get('min', 100):
441
+ pwm_value = 0
442
+ else:
443
+ val_amplitude = conf.get('max', 24) - conf.get('min', 0)
444
+ val_relative = value / val_amplitude
445
+
446
+ duty_max = conf.get('device_max', 24) / 24 * 1023
447
+ duty_min = conf.get('device_min', 0) / 24 * 1023
448
+
449
+ pwm_amplitude = duty_max - duty_min
450
+ pwm_value = duty_min + pwm_amplitude * val_relative
451
+
452
+ return pwm_value
453
+
454
+ def _prepare_for_set(self, pwm_value):
455
+ conf = self.component.config
456
+ duty_max = conf.get('device_max', 24) / 24 * 1023
457
+ duty_min = conf.get('device_min', 0) / 24 * 1023
458
+
459
+ if pwm_value > duty_max:
460
+ value = conf.get('max', 24)
461
+ elif pwm_value < duty_min:
462
+ value = conf.get('min', 0)
463
+ else:
464
+ pwm_amplitude = duty_max - duty_min
465
+ relative_value = (pwm_value - duty_min) / pwm_amplitude
466
+ val_amplitude = conf.get('max', 24) - conf.get('min', 0)
467
+ value = conf.get('min', 0) + val_amplitude * relative_value
468
+
469
+ return round(value, 3)
425
470
 
426
471
 
427
472
  class RGBLight(FleeDeviceMixin, BasicOutputMixin, BaseRGBWLight):
@@ -460,6 +505,25 @@ class DualMotorValve(FleeDeviceMixin, BasicOutputMixin, BaseDimmer):
460
505
 
461
506
  self.component.save()
462
507
 
508
+ def _prepare_for_send(self, value):
509
+ conf = self.component.config
510
+ if value >= conf.get('max', 100):
511
+ value = conf.get('max', 100)
512
+ elif value < conf.get('min', 0):
513
+ value = conf.get('min', 0)
514
+ val_amplitude = conf.get('max', 100) - conf.get('min', 0)
515
+ return ((value - conf.get('min', 0)) / val_amplitude) * 100
516
+
517
+
518
+ def _prepare_for_set(self, value):
519
+ conf = self.component.config
520
+ if value > conf.get('max', 100):
521
+ value = conf.get('max', 100)
522
+ elif value < conf.get('min', 0.0):
523
+ value = conf.get('min', 0)
524
+ val_amplitude = conf.get('max', 100) - conf.get('min', 0)
525
+ return conf.get('min', 0) + (value / 100) * val_amplitude
526
+
463
527
 
464
528
  class Blinds(FleeDeviceMixin, BasicOutputMixin, GenericBlinds):
465
529
  gateway_class = FleetGatewayHandler