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

Binary file
Binary file
Binary file
simo/core/forms.py CHANGED
@@ -347,7 +347,12 @@ class ComponentAdminForm(forms.ModelForm):
347
347
  # not a form submission, don't modify self.fields
348
348
  return
349
349
 
350
- got_keys = data.keys()
350
+ got_keys = list(data.keys())
351
+ formset_fields = set()
352
+ for key in got_keys:
353
+ if key.endswith('-TOTAL_FORMS'):
354
+ formset_fields.add(key[:-12])
355
+ got_keys.extend(list(formset_fields))
351
356
  field_names = self.fields.keys()
352
357
  for missing in set(field_names) - set(got_keys):
353
358
  del self.fields[missing]
simo/core/models.py CHANGED
@@ -68,7 +68,7 @@ def post_icon_delete(sender, instance, *args, **kwargs):
68
68
  pass
69
69
 
70
70
 
71
- class Instance(DirtyFieldsMixin, models.Model, SimoAdminMixin):
71
+ class Instance(models.Model, SimoAdminMixin):
72
72
  # Multiple home instances can be had on a single hub computer!
73
73
  # For example separate hotel apartments
74
74
  # or something of that kind.
simo/core/permissions.py CHANGED
@@ -59,7 +59,7 @@ class InstanceSuperuserCanEdit(BasePermission):
59
59
  if user_role.is_superuser:
60
60
  return True
61
61
 
62
- if user_role.is_owner and request.method == 'PUT':
62
+ if user_role.is_owner and request.method != 'DELETE':
63
63
  return True
64
64
 
65
65
  return False
@@ -76,6 +76,8 @@ class ComponentPermission(BasePermission):
76
76
  user_role = request.user.get_role(view.instance)
77
77
  if user_role.is_superuser:
78
78
  return True
79
+ if user_role.is_owner and request.method != 'DELETE':
80
+ return True
79
81
  if request.method == 'POST' and user_role.component_permissions.filter(
80
82
  write=True, component=obj
81
83
  ).count():
simo/core/serializers.py CHANGED
@@ -261,12 +261,6 @@ class ComponentSerializer(FormSerializer):
261
261
  self.set_form_cls()
262
262
 
263
263
  ret = OrderedDict()
264
- if not self.context['request'].user.is_master:
265
- user_role = self.context['request'].user.get_role(
266
- self.context['instance']
267
- )
268
- if not any([user_role.is_superuser, user_role.is_owner]):
269
- return ret
270
264
 
271
265
  field_mapping = reduce_attr_dict_from_instance(
272
266
  self,
@@ -376,9 +370,11 @@ class ComponentSerializer(FormSerializer):
376
370
  user_role = self.context['request'].user.get_role(
377
371
  self.context['instance']
378
372
  )
373
+ print("FORM BASIC FIELDS: ", form.basic_fields)
379
374
  if not user_role.is_superuser and user_role.is_owner:
380
375
  for field_name in list(form.fields.keys()):
381
376
  if field_name not in form.basic_fields:
377
+ print("DELETE FIELD: ", field_name)
382
378
  del form.fields[field_name]
383
379
  return form
384
380
 
simo/core/tasks.py CHANGED
@@ -331,6 +331,30 @@ def restart_postgresql():
331
331
  proc.communicate()
332
332
 
333
333
 
334
+ @celery_app.task
335
+ def low_battery_notifications():
336
+ from simo.users.models import User
337
+ from simo.notifications.utils import notify_users
338
+ for instance in Instance.objects.all():
339
+ timezone.activate(instance.timezone)
340
+ if timezone.localtime().hour != 10:
341
+ continue
342
+ for comp in Component.objects.filter(
343
+ zone__instance=instance,
344
+ battery_level__isnull=False, battery_level__lt=20
345
+ ):
346
+ users = User.objects.filter(
347
+ roles__is_owner=True, roles__instance=comp.zone.instance
348
+ ).distinct()
349
+ if users:
350
+ notify_users(
351
+ comp.zone.instance, 'warning',
352
+ f"Low battery ({comp.battery_level}%) on {comp}",
353
+ component=comp, users=users
354
+ )
355
+
356
+
357
+
334
358
  @celery_app.on_after_finalize.connect
335
359
  def setup_periodic_tasks(sender, **kwargs):
336
360
  sender.add_periodic_task(1, watch_timers.s())
@@ -339,3 +363,4 @@ def setup_periodic_tasks(sender, **kwargs):
339
363
  sender.add_periodic_task(60 * 60 * 6, update_latest_version_available.s())
340
364
  sender.add_periodic_task(60, drop_fingerprints_learn.s())
341
365
  sender.add_periodic_task(60 * 60 * 24, restart_postgresql.s())
366
+ sender.add_periodic_task(60 * 60, low_battery_notifications.s())
simo/generic/models.py CHANGED
@@ -125,11 +125,27 @@ def bind_controlling_locks_to_alarm_groups(sender, instance, *args, **kwargs):
125
125
  base_type=AlarmGroup.base_type,
126
126
  config__arming_locks__contains=instance.id
127
127
  ):
128
- if ag.config.get(
129
- 'arm_on_away', ''
130
- ).startswith('on_away_and_locked'):
128
+ if ag.config.get('arm_on_away') in (None, 'on_away'):
131
129
  continue
132
- ag.arm()
130
+ users_at_home = InstanceUser.objects.filter(
131
+ instance=instance.instance, at_home=True
132
+ ).exclude(is_active=False).exclude(id=instance.id).count()
133
+ if users_at_home:
134
+ continue
135
+ if ag.config.get('arm_on_away') == 'on_away_and_locked':
136
+ print(f"Nobody is at home, lock was locked. Arm {ag}!")
137
+ ag.arm()
138
+ continue
139
+ locked_states = [
140
+ True if l['value'] == 'locked' else False
141
+ for l in Component.objects.filter(
142
+ base_type='lock', id__in=ag.config.get('arming_locks', []),
143
+ ).values('value')
144
+ ]
145
+ if all(locked_states):
146
+ print(f"Nobody is at home, all locks are now locked. Arm {ag}!")
147
+ ag.arm()
148
+
133
149
  elif instance.value == 'unlocked':
134
150
  for ag in Component.objects.filter(
135
151
  base_type=AlarmGroup.base_type,
@@ -148,8 +164,8 @@ def bind_alarm_groups(sender, instance, created, *args, **kwargs):
148
164
  return
149
165
  users_at_home = InstanceUser.objects.filter(
150
166
  instance=instance.instance, at_home=True
151
- ).exclude(is_active=False).exclude(id=instance.id)
152
- if users_at_home.count():
167
+ ).exclude(is_active=False).exclude(id=instance.id).count()
168
+ if users_at_home:
153
169
  return
154
170
  for ag in Component.objects.filter(
155
171
  zone__instance=instance.instance,
@@ -6,9 +6,7 @@ def notify_users(instance, severity, title, body=None, component=None, users=Non
6
6
  assert severity in ('info', 'warning', 'alarm')
7
7
  notification = Notification.objects.create(
8
8
  instance=instance,
9
- title='<strong>%s:</strong> %s' % (
10
- instance.name, title
11
- ),
9
+ title=f'{instance.name}: {title}',
12
10
  severity=severity, body=body,
13
11
  component=component
14
12
  )
@@ -18,6 +16,9 @@ def notify_users(instance, severity, title, body=None, component=None, users=Non
18
16
  instance_roles__is_active=True
19
17
  )
20
18
  for user in users:
19
+ # do not send emails to system users
20
+ if user.email.endswith('simo.io'):
21
+ continue
21
22
  if instance not in user.instances:
22
23
  continue
23
24
  if component and not component.can_write(user):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: simo
3
- Version: 2.0.26
3
+ Version: 2.0.28
4
4
  Summary: Smart Home on Steroids!
5
5
  Author-email: Simanas Venčkauskas <simanas@simo.io>
6
6
  Project-URL: Homepage, https://simo.io
@@ -38,19 +38,19 @@ simo/core/controllers.py,sha256=iJ7cIGv2WhSGjyxCFfK49pZXuEMfSe75bMEAkRN8G4g,2710
38
38
  simo/core/dynamic_settings.py,sha256=U2WNL96JzVXdZh0EqMPWrxqO6BaRR2Eo5KTDqz7MC4o,1943
39
39
  simo/core/events.py,sha256=LvtonJGNyCb6HLozs4EG0WZItnDwNdtnGQ4vTcnKvUs,4438
40
40
  simo/core/filters.py,sha256=ghtOZcrwNAkIyF5_G9Sn73NkiI71mXv0NhwCk4IyMIM,411
41
- simo/core/forms.py,sha256=tsWBsoBJeUiXDVU6lKbMbnBZORmNrnK5XnoBS_9uWsU,23583
41
+ simo/core/forms.py,sha256=fLPVOZOuHZigeXhuFaliY03-egMKiad1QW91LPpSMA8,23786
42
42
  simo/core/gateways.py,sha256=s_c2W0v2_te89i6LS4Nj7F2wn9UwjZXPT7pfy6SToVo,3714
43
43
  simo/core/loggers.py,sha256=EBdq23gTQScVfQVH-xeP90-wII2DQFDjoROAW6ggUP4,1645
44
44
  simo/core/managers.py,sha256=WoQ4OX3akIvoroSYji-nLVqXBSJzCiC1u_IiWkKbKmA,2413
45
45
  simo/core/middleware.py,sha256=pO52hQOJV_JRmNyUe7zfufSnJFlRITOWX6jwkoPWJhk,2052
46
- simo/core/models.py,sha256=m05fXzOtLrq4LEGknU1mjTHzaTmIEBi1zhwvtDWDknE,19773
47
- simo/core/permissions.py,sha256=TCIHf6kjVAs2mGQ2IV2YRxjHYBsDbl47FLm_wCRKMwY,2618
46
+ simo/core/models.py,sha256=PAVuRYldy4qw7KTsU3ClkEHY7UVvW7CPGGqM3frtvz4,19755
47
+ simo/core/permissions.py,sha256=yqVXq6SNZccSKcOoGdb0oh-WHsyTTtI9ovJdJyhjv28,2707
48
48
  simo/core/routing.py,sha256=X1_IHxyA-_Q7hw1udDoviVP4_FSBDl8GYETTC2zWTbY,499
49
- simo/core/serializers.py,sha256=xL0RHvi20_2qsQzHMN92xhmpp-wmo54UUl9wfkYp61k,18329
49
+ simo/core/serializers.py,sha256=vOrWlfSkmdyaG0VPM0fmRLd7OkkNNGyAkc6Q8Cwa_1Q,18179
50
50
  simo/core/signal_receivers.py,sha256=EZ8NSYZxUgSaLS16YZdK7T__l8dl0joMRllOxx5PUt4,2809
51
51
  simo/core/socket_consumers.py,sha256=n7VE2Fvqt4iEAYLTRbTPOcI-7tszMAADu7gimBxB-Fg,9635
52
52
  simo/core/storage.py,sha256=YlxmdRs-zhShWtFKgpJ0qp2NDBuIkJGYC1OJzqkbttQ,572
53
- simo/core/tasks.py,sha256=4QShdl39MB6InYfBYiGJ167ARKBhO7WHMvU7i-kvsRU,11325
53
+ simo/core/tasks.py,sha256=PEGGsKBbkQOl3LhLfGR5orbYtCxzcv8peMLiVxgl3R0,12228
54
54
  simo/core/todos.py,sha256=eYVXfLGiapkxKK57XuviSNe3WsUYyIWZ0hgQJk7ThKo,665
55
55
  simo/core/types.py,sha256=WJEq48mIbFi_5Alt4wxWMGXxNxUTXqfQU5koH7wqHHI,1108
56
56
  simo/core/views.py,sha256=hlAKpAbCbqI3a-uL5tDp532T2oLFiF0MBzKUJ_SNzo0,5833
@@ -69,19 +69,19 @@ simo/core/__pycache__/controllers.cpython-38.pyc,sha256=40wJ3mu0CnzexxDzXyDIlSxa
69
69
  simo/core/__pycache__/dynamic_settings.cpython-38.pyc,sha256=ELu06Hub4DOidja71ybvD3ZM4HdXiyZjNJrZfnXZXNA,2476
70
70
  simo/core/__pycache__/events.cpython-38.pyc,sha256=A1Axx-qftd1r7st7wkO3DkvTdt9-RkcJe5KJhpzJVk8,5109
71
71
  simo/core/__pycache__/filters.cpython-38.pyc,sha256=VIMADCBiYhziIyRmxAyUDJluZvuZmiC4bNYWTRsGSao,721
72
- simo/core/__pycache__/forms.cpython-38.pyc,sha256=d0ruviTG6zihcc9hJ1TvVxPODwe7LJCyo_ZpLIUt5pI,19850
72
+ simo/core/__pycache__/forms.cpython-38.pyc,sha256=jHbvv2098I6-lWHLg6_G6sxTmXSzk670WophuMGcSzI,19987
73
73
  simo/core/__pycache__/gateways.cpython-38.pyc,sha256=XBiwMfBkjoQ2re6jvADJOwK0_0Aav-crzie9qtfqT9U,4599
74
74
  simo/core/__pycache__/loggers.cpython-38.pyc,sha256=Z-cdQnC6XlIonPV4Sl4E52tP4NMEdPAiHK0cFaIL7I8,1623
75
75
  simo/core/__pycache__/managers.cpython-38.pyc,sha256=5vstOMfm997CZBBkaSiaS7EojhLTWZlbeA_EQ8u-yfg,2554
76
76
  simo/core/__pycache__/middleware.cpython-38.pyc,sha256=ESR5JPtITo9flczO0672sfzYUxrc_cQU0e0w5DFL-60,2038
77
- simo/core/__pycache__/models.cpython-38.pyc,sha256=kYcojazryAAgZCXMoqqutIG2p8ofSKxza9TMx1SR2dQ,17233
78
- simo/core/__pycache__/permissions.cpython-38.pyc,sha256=SV8hZn3Zh-K4NrdtADZ9RlkkHSHTDbScR24Nto8mJXw,2759
77
+ simo/core/__pycache__/models.cpython-38.pyc,sha256=Meg9pm3gCMVgE1a37nEsCtqONMNFeG9R4oPON4CQgcw,17231
78
+ simo/core/__pycache__/permissions.cpython-38.pyc,sha256=flJOCh94U8mFhE0XWzUD0sGR6Xe1HlfG4hQtNSnAGZ4,2788
79
79
  simo/core/__pycache__/routing.cpython-38.pyc,sha256=3T3FPJ8Cn99xZCGvMyg2xjl7al-Shm9CelbSpkJtNP8,599
80
- simo/core/__pycache__/serializers.cpython-38.pyc,sha256=LqjUor0ggXAWSRgTSEdWYGllIOhio8Mlh-kkhBbai30,17237
80
+ simo/core/__pycache__/serializers.cpython-38.pyc,sha256=Pi9lddoS_DtMuA1iZ4mFHu_dG2mjouWxajM4udPQKqo,17185
81
81
  simo/core/__pycache__/signal_receivers.cpython-38.pyc,sha256=sgjH_wv-1U99auH5uHb3or0qettPeHAlsz8P7B03ajY,2430
82
82
  simo/core/__pycache__/socket_consumers.cpython-38.pyc,sha256=NJUr7nRyHFvmAumxxWpsod5wzVVZM99rCEuJs1utHA4,8432
83
83
  simo/core/__pycache__/storage.cpython-38.pyc,sha256=BTkYH8QQyjqI0WOtJC8fHNtgu0YA1vjqZclXjC2vCVI,1116
84
- simo/core/__pycache__/tasks.cpython-38.pyc,sha256=JTqXI_QPhU8u5c8MdhxILLKcn-5ZlIwtG1mGO4bgB7A,8474
84
+ simo/core/__pycache__/tasks.cpython-38.pyc,sha256=8VyzcG4psw5cS2oTy8W7EVW_nP7fzhoMvxXczTe1rcw,9134
85
85
  simo/core/__pycache__/todos.cpython-38.pyc,sha256=lOqGZ58siHM3isoJV4r7sg8igrfE9fFd-jSfeBa0AQI,253
86
86
  simo/core/__pycache__/views.cpython-38.pyc,sha256=YrKRZPaV_JM4VGpdhVhsK-90UwUTOqp-V-Yj0SRGZgs,4212
87
87
  simo/core/__pycache__/widgets.cpython-38.pyc,sha256=sR0ZeHCHrhnNDBJuRrxp3zUsfBp0xrtF0xrK2TkQv1o,3520
@@ -10278,7 +10278,7 @@ simo/generic/base_types.py,sha256=djymox_boXTHX1BTTCLXrCH7ED-uAsV_idhaDOc3OLI,40
10278
10278
  simo/generic/controllers.py,sha256=WYuOUzDWvkYRaTvlbdGy_qmwp1o_ohqKDfV7OrOq2QU,52218
10279
10279
  simo/generic/forms.py,sha256=uLZ25Fw1xGqyDBngWPve9EaHpyldFegY_TPBKV4Oby0,24051
10280
10280
  simo/generic/gateways.py,sha256=b3tQ2bAkDVYXCF5iZi2yi-6nZAM8WmHE9ICwxMyR0to,17034
10281
- simo/generic/models.py,sha256=ni1cgqVZ8ttk1snW26EB-2dqsHRZkLg6463my_Cu3EU,6618
10281
+ simo/generic/models.py,sha256=92TACMhJHadAg0TT9GnARO_R3_Sl6i-GGjhG_x7YdFI,7391
10282
10282
  simo/generic/routing.py,sha256=elQVZmgnPiieEuti4sJ7zITk1hlRxpgbotcutJJgC60,228
10283
10283
  simo/generic/socket_consumers.py,sha256=NfTQGYtVAc864IoogZRxf_0xpDPM0eMCWn0SlKA5P7Y,1751
10284
10284
  simo/generic/__pycache__/__init__.cpython-38.pyc,sha256=mLu54WS9KIl-pHwVCBKpsDFIlOqml--JsOVzAUHg6cU,161
@@ -10287,7 +10287,7 @@ simo/generic/__pycache__/base_types.cpython-38.pyc,sha256=ptw6axyAqemZA35oa6vzr7
10287
10287
  simo/generic/__pycache__/controllers.cpython-38.pyc,sha256=e0bvgyePgJbIs1omBq0TRPlVSKar2sK_JbUKqDRj7mY,33235
10288
10288
  simo/generic/__pycache__/forms.cpython-38.pyc,sha256=U67jiwt6R8J547n7d84-_9mUxCXxKl-L0VFdYfo1aiM,17787
10289
10289
  simo/generic/__pycache__/gateways.cpython-38.pyc,sha256=a4lLIMPyxm9tNzIqorXHIPZFVTcXlPsM1ycJMghxcHA,12673
10290
- simo/generic/__pycache__/models.cpython-38.pyc,sha256=yr64EX5n9u9uaOHNbPhNIBoBJo4Y3V_lvDj1ggMAZCQ,5123
10290
+ simo/generic/__pycache__/models.cpython-38.pyc,sha256=PzlZsM1jxo3FVb7QDm3bny8UFwTsGrMQe4mj4tJ06eQ,5675
10291
10291
  simo/generic/__pycache__/routing.cpython-38.pyc,sha256=xtxTUTBTdivzFyA5Wh7k-hUj1WDO_FiRq6HYXdbr9Ks,382
10292
10292
  simo/generic/__pycache__/socket_consumers.cpython-38.pyc,sha256=piFHces0J9QuXu_CNBCQCYjoZEeoaxyVjLfJ9KaR8C8,1898
10293
10293
  simo/generic/static/weather_icons/01d@2x.png,sha256=TZfWi6Rfddb2P-oldWWcjUiuCHiU9Yrc5hyrQAhF26I,948
@@ -10348,7 +10348,7 @@ simo/notifications/admin.py,sha256=y_gmHYXbDh98LUUa-lp9DilTIgM6-pIujWPQPLQsJo8,8
10348
10348
  simo/notifications/api.py,sha256=GXQpq68ULBaJpU8w3SJKaCKuxYGWYehKnGeocGB1RVc,1783
10349
10349
  simo/notifications/models.py,sha256=VZcvweii59j89nPKlWeUSJ44Qz3ZLjJ6mXN6uB9F1Sw,2506
10350
10350
  simo/notifications/serializers.py,sha256=altDEAPWwOhxRcEzE9-34jL8EFpyf3vPoEdAPoVLfGc,523
10351
- simo/notifications/utils.py,sha256=5CtKOvX0vbWLXFvJD_8WfWulMEp1FuMwrfCGDLHySdA,907
10351
+ simo/notifications/utils.py,sha256=VCBKcHrtZrdCalKe2w7umt7-trhG3vw-fKn1ndyr3CA,971
10352
10352
  simo/notifications/__pycache__/__init__.cpython-38.pyc,sha256=YvucUfu98XFvEEg1LYFMlOZJpo_jSGxTVrM-ylAFLOg,167
10353
10353
  simo/notifications/__pycache__/admin.cpython-38.pyc,sha256=Fl4crSZTFQOTYQioV6ff9fBRV4MhNiwQgMS2VnmCI4M,1632
10354
10354
  simo/notifications/__pycache__/api.cpython-38.pyc,sha256=ys6E4AFghX6bq-rQ0gtA9s0Y2Hh-ypsWH8-Yz4edMrc,2073
@@ -10456,8 +10456,8 @@ simo/users/templates/invitations/expired_msg.html,sha256=47DEQpj8HBSa-_TImW-5JCe
10456
10456
  simo/users/templates/invitations/expired_suggestion.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10457
10457
  simo/users/templates/invitations/taken_msg.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10458
10458
  simo/users/templates/invitations/taken_suggestion.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10459
- simo-2.0.26.dist-info/LICENSE.md,sha256=M7wm1EmMGDtwPRdg7kW4d00h1uAXjKOT3HFScYQMeiE,34916
10460
- simo-2.0.26.dist-info/METADATA,sha256=7QlteBUjfDoMYB7h3GQspaQINFPXrri9o-vxvt9QPQY,1730
10461
- simo-2.0.26.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
10462
- simo-2.0.26.dist-info/top_level.txt,sha256=GmS1hrAbpVqn9OWZh6UX82eIOdRLgYA82RG9fe8v4Rs,5
10463
- simo-2.0.26.dist-info/RECORD,,
10459
+ simo-2.0.28.dist-info/LICENSE.md,sha256=M7wm1EmMGDtwPRdg7kW4d00h1uAXjKOT3HFScYQMeiE,34916
10460
+ simo-2.0.28.dist-info/METADATA,sha256=_YtDqotbhb9Tmq6gjFTcocuHWtFdM-7pcy2H10cipLQ,1730
10461
+ simo-2.0.28.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
10462
+ simo-2.0.28.dist-info/top_level.txt,sha256=GmS1hrAbpVqn9OWZh6UX82eIOdRLgYA82RG9fe8v4Rs,5
10463
+ simo-2.0.28.dist-info/RECORD,,
File without changes