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

@@ -257,23 +257,43 @@ class AutomationsGatewayHandler(GatesHandler, BaseObjectCommandsGatewayHandler):
257
257
  # however the process is actually still running
258
258
  process.kill()
259
259
  self.last_death = time.time()
260
- self.running_scripts.pop(id, None) # no longer running for sure!
261
- if not comp or comp.value != 'running':
262
- continue
263
-
264
- if id not in self.terminating_scripts: # was not intentionaly terminated
265
- if comp:
260
+ # If component exists and is marked running, attempt to persist error
261
+ # BEFORE removing from in-memory tracking, so we can retry if DB is down.
262
+ if comp and comp.value == 'running' and id not in self.terminating_scripts:
263
+ try:
266
264
  tz = pytz.timezone(comp.zone.instance.timezone)
267
265
  timezone.activate(tz)
268
- logger = get_component_logger(comp)
269
- logger.log(logging.INFO, "-------DEAD!-------")
270
- comp.value = 'error'
271
- comp.save()
266
+ logger = get_component_logger(comp)
267
+ logger.log(logging.INFO, "-------DEAD!-------")
268
+ comp.value = 'error'
269
+ comp.save()
270
+ except Exception:
271
+ # Leave entry in running_scripts to retry on next tick
272
+ continue
273
+ # For any other case or after successful DB update, drop tracking entry
274
+ self.running_scripts.pop(id, None)
272
275
 
273
276
  if self.last_death and time.time() - self.last_death < 5:
274
277
  # give 10s air before we wake these dead scripts up!
275
278
  return
276
279
 
280
+ # Reconcile scripts marked as 'running' in DB but not tracked or with dead PID
281
+ for comp in Component.objects.filter(base_type='script', value='running'):
282
+ if comp.id in self.running_scripts:
283
+ continue
284
+ pid = None
285
+ try:
286
+ pid = int(comp.meta.get('pid')) if comp.meta and 'pid' in comp.meta else None
287
+ except Exception:
288
+ pid = None
289
+ is_pid_alive = bool(pid) and os.path.exists(f"/proc/{pid}")
290
+ if not is_pid_alive:
291
+ try:
292
+ comp.value = 'error'
293
+ comp.save(update_fields=['value'])
294
+ except Exception:
295
+ pass
296
+
277
297
  for script in Component.objects.filter(
278
298
  base_type='script', config__keep_alive=True
279
299
  ).exclude(value__in=('running', 'stopped', 'finished')):
@@ -357,13 +377,9 @@ class AutomationsGatewayHandler(GatesHandler, BaseObjectCommandsGatewayHandler):
357
377
  print("START SCRIPT %s" % str(component))
358
378
 
359
379
  if component.id in self.running_scripts:
360
- # it appears that the script is running and is perfectly healthy
361
- # so we make sure it has correct value, do nothing else and return!
380
+ # Script appears to be healthy; do nothing and return.
362
381
  if component.id not in self.terminating_scripts \
363
382
  and self.running_scripts[component.id]['proc'].is_alive():
364
- if component.value != 'running':
365
- component.value = 'running'
366
- component.save()
367
383
  return
368
384
 
369
385
  # script is in terminating state or is no longer alive
@@ -429,4 +445,4 @@ class AutomationsGatewayHandler(GatesHandler, BaseObjectCommandsGatewayHandler):
429
445
  self.running_scripts.pop(component.id, None)
430
446
  logger.handlers = []
431
447
 
432
- threading.Thread(target=kill, daemon=True).start()
448
+ threading.Thread(target=kill, daemon=True).start()
simo/fleet/controllers.py CHANGED
@@ -991,6 +991,8 @@ class TempHumSensor(FleetDeviceMixin, BasicSensorMixin, BaseMultiSensor):
991
991
  ['temperature', 0, self.sys_temp_units],
992
992
  ['humidity', 20, '%'],
993
993
  ['real_feel', 0, self.sys_temp_units],
994
+ ['temp_raw', 0, 'C'],
995
+ ['hum_raw', 0, '%'],
994
996
  ['core', 0, 'C'],
995
997
  ['outside', 0, 'C']
996
998
  ]
@@ -1015,6 +1017,8 @@ class TempHumSensor(FleetDeviceMixin, BasicSensorMixin, BaseMultiSensor):
1015
1017
  ['temperature', temp, self.sys_temp_units],
1016
1018
  ['humidity', humidity, '%'],
1017
1019
  ['real_feel', 0, self.sys_temp_units],
1020
+ ['temp_raw', value.get('temp_raw'), 'C'],
1021
+ ['hum_raw', value.get('hum_raw'), '%'],
1018
1022
  ['core', value.get('core'), 'C'],
1019
1023
  ['outside', value.get('out'), 'C']
1020
1024
  ]
@@ -1034,6 +1038,7 @@ class AmbientLightSensor(FleetDeviceMixin, BaseNumericSensor):
1034
1038
  gateway_class = FleetGatewayHandler
1035
1039
  name = "Ambient lighting sensor"
1036
1040
  manual_add = False
1041
+ default_value_units = 'lux'
1037
1042
  default_config = {
1038
1043
  'widget': 'numeric-sensor',
1039
1044
  'value_units': 'lux',
@@ -1045,6 +1050,7 @@ class AmbientLightSensor(FleetDeviceMixin, BaseNumericSensor):
1045
1050
  }
1046
1051
 
1047
1052
 
1053
+
1048
1054
  class RoomPresenceSensor(FleetDeviceMixin, BaseBinarySensor):
1049
1055
  gateway_class = FleetGatewayHandler
1050
1056
  name = "Human presence sensor"
@@ -1148,6 +1154,12 @@ class RoomZonePresenceSensor(FleetDeviceMixin, BaseBinarySensor):
1148
1154
  ).publish()
1149
1155
 
1150
1156
 
1157
+ class SmokeDetector(FleetDeviceMixin, BaseBinarySensor):
1158
+ name = _("Dust/pollution detector")
1159
+ gateway_class = FleetGatewayHandler
1160
+ manual_add = False
1161
+
1162
+
1151
1163
  class VoiceAssistant(FleetDeviceMixin, BaseBinarySensor):
1152
1164
  base_type = VoiceAssistantType
1153
1165
  name = _("AI Voice Assistant")
simo/fleet/forms.py CHANGED
@@ -1815,7 +1815,7 @@ class SentinelDeviceConfigForm(BaseComponentForm):
1815
1815
 
1816
1816
  from .controllers import (
1817
1817
  RoomSiren, AirQualitySensor, TempHumSensor, AmbientLightSensor,
1818
- RoomPresenceSensor, VoiceAssistant
1818
+ RoomPresenceSensor, VoiceAssistant, SmokeDetector
1819
1819
  )
1820
1820
 
1821
1821
  org_name = self.cleaned_data['name']
@@ -1827,7 +1827,8 @@ class SentinelDeviceConfigForm(BaseComponentForm):
1827
1827
  (TempHumSensor, 'temperature-half', 'temperature', 'climate'),
1828
1828
  (AmbientLightSensor, 'brightness-low', 'brightness', 'light'),
1829
1829
  (RoomPresenceSensor, 'person', 'presence', 'security'),
1830
- (VoiceAssistant, 'microphone-lines', 'voice assistant', 'other')
1830
+ (VoiceAssistant, 'microphone-lines', 'voice assistant', 'other'),
1831
+ (SmokeDetector, 'fire-smoke', 'dust/pollution', 'security'),
1831
1832
  ):
1832
1833
  default_icon = Icon.objects.filter(slug=icon).first()
1833
1834
  self.cleaned_data['icon'] = default_icon.slug if default_icon else org_icon
@@ -1847,6 +1848,7 @@ class SentinelDeviceConfigForm(BaseComponentForm):
1847
1848
  )
1848
1849
  if form.is_valid():
1849
1850
  comp = form.save()
1851
+ comp.value_units = CtrlClass.default_value_units
1850
1852
  comp.config['colonel'] = colonel.id
1851
1853
  comp.save()
1852
1854
  last_comp = comp
@@ -65,6 +65,11 @@ class VoiceAssistantSession:
65
65
  return
66
66
  self.active = True
67
67
  self.started_ts = time.time()
68
+ # Ensure a fresh session starts gated until arbitration grants winner
69
+ try:
70
+ self._cloud_gate.clear()
71
+ except Exception:
72
+ pass
68
73
  # is_vo_active will be set by arbitration via open_as_winner
69
74
 
70
75
  async def on_audio_chunk(self, payload: bytes):
@@ -668,6 +673,11 @@ class VoiceAssistantSession:
668
673
  self._start_session_notified = False
669
674
  self._start_session_inflight = False
670
675
  self._prewarm_requested = False
676
+ # Close cloud gate so subsequent sessions don't bypass arbitration
677
+ try:
678
+ self._cloud_gate.clear()
679
+ except Exception:
680
+ pass
671
681
  for t in (self._finalizer_task, self._cloud_task, self._play_task, self._followup_task):
672
682
  if t and not t.done():
673
683
  t.cancel()
@@ -147,7 +147,7 @@ class Thermostat(ControllerBase):
147
147
  target_temp = sorted_options[-1][1]
148
148
  for timestr, target in sorted_options:
149
149
  start_second = int(timestr.split(':')[0]) * 3600 \
150
- + int(timestr.split(':')[1] * 60)
150
+ + int(timestr.split(':')[1]) * 60
151
151
  if start_second < current_second:
152
152
  target_temp = target
153
153
  return target_temp
@@ -364,7 +364,7 @@ class Thermostat(ControllerBase):
364
364
  self._get_default_user_config()
365
365
  )
366
366
  self.component.save()
367
- self.evaluate()
367
+ self._evaluate()
368
368
 
369
369
 
370
370
  def hold(self, temperature=None):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: simo
3
- Version: 3.0.1
3
+ Version: 3.0.4
4
4
  Summary: Smart Home Supremacy
5
5
  Author-email: "Simon V." <simon@simo.io>
6
6
  Project-URL: Homepage, https://simo.io
@@ -33,7 +33,6 @@ Requires-Dist: itsdangerous==2.0.1
33
33
  Requires-Dist: redis==3.5.3
34
34
  Requires-Dist: django-redis==4.12.1
35
35
  Requires-Dist: webservices==0.7
36
- Requires-Dist: websockets==13.1
37
36
  Requires-Dist: numpy>=1.24.4
38
37
  Requires-Dist: geopy==2.2.0
39
38
  Requires-Dist: requests>=2.26.0
@@ -21,7 +21,7 @@ simo/automation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
21
  simo/automation/app_widgets.py,sha256=gaqImMZjuMHm7nIb9a4D-Y3qipz_WhSPAHXcwGx4Uzs,199
22
22
  simo/automation/controllers.py,sha256=jtoG91nYlUPW-Pkl025bBoGELWFm8SQD3duTPsjRhfk,12230
23
23
  simo/automation/forms.py,sha256=NAQUS9qrXp2d4GuvdpVyWj0Yh7vqMyX6pzs5oX4ze5Y,9800
24
- simo/automation/gateways.py,sha256=L97hTJJgKneBw38pQQ4UwSk26FgxVzQ5tK2eQPAK4m8,16577
24
+ simo/automation/gateways.py,sha256=nqqiNFTYmHnwyYCM2tI-AS4gCnEFPVoJojoMo2mytVM,17383
25
25
  simo/automation/helpers.py,sha256=iP-fxxB8HsFQy3k2CjFubu86aMqvWgmh-p24DiyOrek,4330
26
26
  simo/automation/models.py,sha256=zt-jkzyq5ddqGT864OkJzCsvov2vZ0nO4ez3hAeZkXg,934
27
27
  simo/automation/serializers.py,sha256=Pg-hMaASQPB5_BTAMkfqM6z4jdHWH8xMYWOvDxIvmx8,2126
@@ -10456,8 +10456,8 @@ simo/fleet/apps.py,sha256=je8mRXMcRq4lABQZlyF2G2hOCkBUicR9I2jvrLDA8eI,238
10456
10456
  simo/fleet/auto_urls.py,sha256=vrfrooPyY4pDuQjya-eLxCgZldfhwbEeEiXa7diO_CY,847
10457
10457
  simo/fleet/base_types.py,sha256=ksJAx-BzUJzv7VvfVxRtDybypHYKPG33UedWRGTBA_4,1243
10458
10458
  simo/fleet/ble.py,sha256=eHA_9ABjbmH1vUVCv9hiPXQL2GZZSEVwfO0xyI1S0nI,1081
10459
- simo/fleet/controllers.py,sha256=YD7BO1-DFPtCqZVGUDNXNx6DQD5FEiqGHB6I7Xq2nvU,41148
10460
- simo/fleet/forms.py,sha256=1qI_SpYAmFmKfm0F18XTs8bJjRfdNVFLaDGZOtjMGhU,68840
10459
+ simo/fleet/controllers.py,sha256=BExF9Aw5hUf8JWZwuly7aPqR5pQw1V5AMAAO0JE9WLg,41516
10460
+ simo/fleet/forms.py,sha256=9qJNkItANlZdKBPAUdM00ITTKf0nzf5OTS5P9whwFyE,68994
10461
10461
  simo/fleet/gateways.py,sha256=fmtMwoGcJr4ZWjwYvCpYZIr2WdZ2-Lc0KtiKJtfOSQg,6747
10462
10462
  simo/fleet/managers.py,sha256=DKU9kv5S6dAqAHWq4OgfEOeK5IJaQW7qdCednA0NpUA,858
10463
10463
  simo/fleet/models.py,sha256=itSd-v-t8SXVwxXJKCpL8oYwsNeOXAOqfLAOEdM2AVQ,20558
@@ -10467,7 +10467,7 @@ simo/fleet/socket_consumers.py,sha256=PBDQphnIQFZ5ThBJmRh2Fxf_k0RITmFktQAPFftOjs
10467
10467
  simo/fleet/tasks.py,sha256=OYiyhrASyTgLxifhjW9Cx4IaEK6hd1bEXbPGfjIg-zY,1323
10468
10468
  simo/fleet/utils.py,sha256=Ev6jl2oW6Gl7O3KwPBi1LDU1gvNpARchcRI4LQZlrEo,9564
10469
10469
  simo/fleet/views.py,sha256=3F8im6BsSOaK3KEuBNESE4sDbS_dWHYaOdhTR4cCLjE,5189
10470
- simo/fleet/voice_assistant.py,sha256=dkCDYyiEBXaOcmap8DgDS3UhIBahgyh08RJUFd2zlK4,40049
10470
+ simo/fleet/voice_assistant.py,sha256=pEzSLjaMIubP66ozFXaFMHJCZ-ik0oni70-2ePdg2Eg,40388
10471
10471
  simo/fleet/__pycache__/__init__.cpython-312.pyc,sha256=WgOt0Rf4Q4Kz0CyQn-BwX6qKyVcylu4UR1R1bqNKmA0,123
10472
10472
  simo/fleet/__pycache__/__init__.cpython-38.pyc,sha256=pIZE7EL6-cuJ3pQtaSwjKLrKLsTYelp1k9sRhXKLh6s,159
10473
10473
  simo/fleet/__pycache__/admin.cpython-312.pyc,sha256=-gV2FumLvfZJXhfeRKNI0FxYoQcIbBp_krQlRoulgQs,9997
@@ -10670,7 +10670,7 @@ simo/fleet/templates/fleet/controllers_info/RoomZonePresenceSensor.md,sha256=Nun
10670
10670
  simo/generic/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10671
10671
  simo/generic/app_widgets.py,sha256=y8W3jR76Hh26O9pPQyg2SophMbYIOtAWD33MPKbB8Mg,856
10672
10672
  simo/generic/base_types.py,sha256=gJUJYpd_gE-f1ogzagAPA1u2TYljhyU0_SMlgGUvCVk,2318
10673
- simo/generic/controllers.py,sha256=VbaYmK3XUK3g_M1_6p1KHsrPGQ7TNlk7rZAd5dbEMvY,53272
10673
+ simo/generic/controllers.py,sha256=oQ7mLYU-MxO4lpCQEGJA0ClIw253Ayzx3pOAFbkqC_4,53273
10674
10674
  simo/generic/forms.py,sha256=0RIDtLLzCkiSb9OxlioicOQW9yp1OjLKekpjbxzGVfM,26272
10675
10675
  simo/generic/gateways.py,sha256=9K4KkTn0rHRsRba1o8hIQFtRpRtpoL2E33Vozcy-FzM,19458
10676
10676
  simo/generic/models.py,sha256=59fkYowOX0imviIhA6uwupvuharrpBykmBm674rJNoI,7279
@@ -11030,9 +11030,9 @@ simo/users/templates/invitations/expired_msg.html,sha256=47DEQpj8HBSa-_TImW-5JCe
11030
11030
  simo/users/templates/invitations/expired_suggestion.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11031
11031
  simo/users/templates/invitations/taken_msg.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11032
11032
  simo/users/templates/invitations/taken_suggestion.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11033
- simo-3.0.1.dist-info/licenses/LICENSE.md,sha256=M7wm1EmMGDtwPRdg7kW4d00h1uAXjKOT3HFScYQMeiE,34916
11034
- simo-3.0.1.dist-info/METADATA,sha256=mf8ahF_UG3Z9Fkfvuyj_li4r7WKICp5Ax_CCCut0DsQ,2256
11035
- simo-3.0.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
11036
- simo-3.0.1.dist-info/entry_points.txt,sha256=S9PwnUYmTSW7681GKDCxUbL0leRJIaRk6fDQIKgbZBA,135
11037
- simo-3.0.1.dist-info/top_level.txt,sha256=GmS1hrAbpVqn9OWZh6UX82eIOdRLgYA82RG9fe8v4Rs,5
11038
- simo-3.0.1.dist-info/RECORD,,
11033
+ simo-3.0.4.dist-info/licenses/LICENSE.md,sha256=M7wm1EmMGDtwPRdg7kW4d00h1uAXjKOT3HFScYQMeiE,34916
11034
+ simo-3.0.4.dist-info/METADATA,sha256=97WF3o8HGc6oNgTGXMBr8SYV-6A3eiYNhpBvmbKWmbA,2224
11035
+ simo-3.0.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
11036
+ simo-3.0.4.dist-info/entry_points.txt,sha256=S9PwnUYmTSW7681GKDCxUbL0leRJIaRk6fDQIKgbZBA,135
11037
+ simo-3.0.4.dist-info/top_level.txt,sha256=GmS1hrAbpVqn9OWZh6UX82eIOdRLgYA82RG9fe8v4Rs,5
11038
+ simo-3.0.4.dist-info/RECORD,,
File without changes