Better dynamic entities discovery

This commit is contained in:
Fabio Manganiello 2022-04-13 11:25:14 +02:00
parent 332c91252c
commit e6bfa1c50f
Signed by: blacklight
GPG key ID: D90FBA7F76362774
5 changed files with 43 additions and 37 deletions

View file

@ -143,44 +143,30 @@ export default {
},
async refresh() {
const actions = Object.keys(
Object.values(this.selector.selectedEntities).reduce((obj, entity) => {
if (entity.plugin)
obj[entity.plugin] = true
return obj
}, {})
).map((plugin) => `${plugin}.status`)
this.loadingEntities = Object.entries(this.entities).reduce((obj, [id, entity]) => {
const self = this
if (this.entityTimeouts[id])
clearTimeout(this.entityTimeouts[id])
this.loadingEntities = {
...this.loadingEntities,
...Object.keys(this.selector.selectedEntities).reduce((obj, id) => {
const self = this
const entity = this.entities[id]
this.entityTimeouts[id] = setTimeout(() => {
if (self.loadingEntities[id])
delete self.loadingEntities[id]
if (self.entityTimeouts[id])
delete self.entityTimeouts[id]
if (this.entityTimeouts[id])
clearTimeout(this.entityTimeouts[id])
self.errorEntities[id] = entity
self.notify({
error: true,
title: entity.plugin,
text: `Scan timeout for ${entity.name}`,
})
}, this.entityScanTimeout * 1000)
this.entityTimeouts[id] = setTimeout(() => {
if (self.loadingEntities[id])
delete self.loadingEntities[id]
if (self.entityTimeouts[id])
delete self.entityTimeouts[id]
obj[id] = true
return obj
}, {})
self.errorEntities[id] = entity
self.notify({
error: true,
title: entity.plugin,
text: `Scan timeout for ${entity.name}`,
})
}, this.entityScanTimeout * 1000)
obj[id] = true
return obj
}, {}),
}
// Force refresh by calling `.status` on all the selected plugins
await Promise.all(actions.map((act) => this.request(act)))
await this.request('entities.scan')
},
async sync() {
@ -231,6 +217,7 @@ export default {
...event.entity,
meta: {
...(this.entities[entityId]?.meta || {}),
...(meta[event.entity.type] || {}),
...(event.entity?.meta || {}),
},
}

View file

@ -1,3 +1,4 @@
import json
from logging import getLogger
from queue import Queue, Empty
from threading import Thread, Event, RLock
@ -23,6 +24,7 @@ class EntitiesEngine(Thread):
self.logger = getLogger(name=obj_name)
self._queue = Queue()
self._should_stop = Event()
self._entities_awaiting_flush = set()
self._entities_cache_lock = RLock()
self._entities_cache = {
'by_id': {},
@ -110,6 +112,16 @@ class EntitiesEngine(Thread):
self._populate_entity_id_from_cache(entity)
if entity.id:
get_bus().post(EntityUpdateEvent(entity=entity))
else:
self._entities_awaiting_flush.add(self._to_entity_awaiting_flush(entity))
@staticmethod
def _to_entity_awaiting_flush(entity: Entity):
e = entity.to_json()
return json.dumps(
{k: v for k, v in e.items() if k in {'external_id', 'name', 'plugin'}},
sort_keys=True,
)
def post(self, *entities: Entity):
for entity in entities:
@ -226,3 +238,10 @@ class EntitiesEngine(Thread):
with self._entities_cache_lock:
for entity in entities:
self._cache_entities(entity, overwrite_cache=True)
entities_awaiting_flush = {*self._entities_awaiting_flush}
for entity in entities:
e = self._to_entity_awaiting_flush(entity)
if e in entities_awaiting_flush:
self._process_event(entity)
self._entities_awaiting_flush.remove(e)

View file

@ -6,7 +6,7 @@ from ._base import Entity
class Device(Entity):
__tablename__ = 'device'
id = Column(Integer, ForeignKey(Entity.id), primary_key=True)
id = Column(Integer, ForeignKey(Entity.id, ondelete='CASCADE'), primary_key=True)
__mapper_args__ = {
'polymorphic_identity': __tablename__,

View file

@ -6,7 +6,7 @@ from .devices import Device
class Light(Device):
__tablename__ = 'light'
id = Column(Integer, ForeignKey(Device.id), primary_key=True)
id = Column(Integer, ForeignKey(Device.id, ondelete='CASCADE'), primary_key=True)
__mapper_args__ = {
'polymorphic_identity': __tablename__,

View file

@ -6,7 +6,7 @@ from .devices import Device
class Switch(Device):
__tablename__ = 'switch'
id = Column(Integer, ForeignKey(Device.id), primary_key=True)
id = Column(Integer, ForeignKey(Device.id, ondelete='CASCADE'), primary_key=True)
state = Column(Boolean)
__mapper_args__ = {