Skip to content

Commit c5f435b

Browse files
authored
Simplified Device Attributes Update Event (#206)
* core/device: Simplify device-update event triggering logic * frontend: Remove device polling mechanism * frontend: Fix linting issues * frontend: Fix remarks from review * core/api: Bump version to 1.3
1 parent 5fe84cd commit c5f435b

11 files changed

Lines changed: 47 additions & 252 deletions

File tree

qtoggleserver/core/api/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from qtoggleserver.web import APIHandler
1010

1111

12-
API_VERSION = "1.1"
12+
API_VERSION = "1.3"
1313

1414
ACCESS_LEVEL_ADMIN = 30
1515
ACCESS_LEVEL_NORMAL = 20

qtoggleserver/core/device/attrs.py

Lines changed: 15 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030

3131

3232
EMPTY_PASSWORD_HASH = hashlib.sha256(b"").hexdigest()
33-
NETWORK_ATTRS_WATCH_INTERVAL = 5
33+
ATTRS_UPDATE_INTERVAL = 5
3434
ATTRDEF_CALLABLE_FIELDS = {"modifiable", "min", "max", "enabled"}
3535

3636

@@ -47,7 +47,7 @@
4747
_schema: GenericJSONDict | None = None
4848
_attrdefs_cache: AttributeDefinitions | None = None
4949
_to_json_attrdefs_cache: AttributeDefinitions | None = None
50-
_attrs_watch_task: asyncio.Task | None = None
50+
_attrs_update_task: asyncio.Task | None = None
5151
_attrs_cache: Attributes | None = None
5252

5353

@@ -906,62 +906,32 @@ async def to_json() -> GenericJSONDict:
906906
return result
907907

908908

909-
def _check_net_data_changed(data: dict) -> bool:
910-
changed = False
911-
912-
if system.net.has_wifi_support():
913-
wifi_config = system.net.get_wifi_config()
914-
old_wifi_config = data.get("wifi_config")
915-
if old_wifi_config != wifi_config:
916-
data["wifi_config"] = wifi_config
917-
changed = True
918-
919-
if system.net.has_ip_support():
920-
ip_config = system.net.get_ip_config()
921-
old_ip_config = data.get("ip_config")
922-
if old_ip_config != ip_config:
923-
data["ip_config"] = ip_config
924-
changed = True
925-
926-
return changed
927-
928-
929-
async def _attrs_watch_loop() -> None:
930-
# TODO: also watch dynamic attributes
931-
932-
last_net_data = {}
933-
909+
async def _attrs_update_loop() -> None:
934910
try:
935911
while True:
936-
changed = False
937912
try:
938-
if _check_net_data_changed(last_net_data):
939-
logger.debug("network attributes data changed")
940-
changed = True
941-
except Exception as e:
942-
logger.error("network attributes data check failed: %s", e, exc_info=True)
943-
944-
if changed:
945913
invalidate_attrs()
946914
await device_events.trigger_update()
947-
948-
await asyncio.sleep(NETWORK_ATTRS_WATCH_INTERVAL)
915+
except Exception:
916+
logger.exception("Error updating attributes")
917+
finally:
918+
await asyncio.sleep(ATTRS_UPDATE_INTERVAL)
949919
except asyncio.CancelledError:
950-
logger.debug("attributes watch task cancelled")
920+
logger.debug("attributes update task cancelled")
951921

952922

953923
async def init() -> None:
954-
global _attrs_watch_task
924+
global _attrs_update_task
955925

956-
logger.debug("starting attributes watch task")
957-
_attrs_watch_task = asyncio.create_task(_attrs_watch_loop())
926+
logger.debug("starting attributes update task")
927+
_attrs_update_task = asyncio.create_task(_attrs_update_loop())
958928

959929

960930
async def cleanup() -> None:
961-
logger.debug("stopping attributes watch task")
962-
if _attrs_watch_task:
963-
_attrs_watch_task.cancel()
931+
logger.debug("stopping attributes update task")
932+
if _attrs_update_task:
933+
_attrs_update_task.cancel()
964934
try:
965-
await _attrs_watch_task
935+
await _attrs_update_task
966936
except asyncio.CancelledError:
967937
pass

qtoggleserver/frontend/js/cache.js

Lines changed: 0 additions & 156 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,11 @@ import {gettext} from '$qui/base/i18n.js'
1010
import Config from '$qui/config.js'
1111
import * as Toast from '$qui/messages/toast.js'
1212
import Debouncer from '$qui/utils/debouncer.js'
13-
import {asap} from '$qui/utils/misc.js'
1413
import * as ObjectUtils from '$qui/utils/object.js'
1514
import * as PromiseUtils from '$qui/utils/promise.js'
16-
import * as Window from '$qui/window.js'
1715

1816
import * as AuthAPI from '$app/api/auth.js'
1917
import * as APIConstants from '$app/api/constants.js'
20-
import * as BaseAPI from '$app/api/base.js'
2118
import * as DevicesAPI from '$app/api/devices.js'
2219
import * as PortsAPI from '$app/api/ports.js'
2320
import * as PrefsAPI from '$app/api/prefs.js'
@@ -27,20 +24,6 @@ import * as NotificationsAPI from '$app/api/notifications.js'
2724
import {getGlobalProgressMessage} from '$app/common/common.js'
2825

2926

30-
const DEVICE_POLL_INTERVAL = 5 /* Seconds */
31-
32-
/* When actively polling a device, only update some selected attributes */
33-
const DEVICE_POLLED_ATTRIBUTES = [
34-
'date',
35-
'uptime',
36-
'wifi_signal_strength',
37-
'temperature',
38-
'cpu_usage',
39-
'mem_usage',
40-
'storage_usage',
41-
'battery_level'
42-
]
43-
4427
const STORAGE_KEY_PREFIX = 'cache'
4528
const STORAGE_SET_DEBOUNCE_DELAY = 5000
4629
const SAVE_PREFS_DEBOUNCE_DELAY = 1000
@@ -70,9 +53,6 @@ const savePrefsDebouncer = new Debouncer(() => {
7053
/* Indicates that cache needs a reload asap */
7154
let reloadNeeded = false
7255

73-
/* The name of a device to be continuously polled */
74-
let polledDeviceName = null
75-
7656
/* Debouncers for cache setters */
7757
const slaveDevicesSetLocalStorageCacheDebouncer = new Debouncer((slaveDevices) => {
7858
logger.debug('saving slave devices to cache')
@@ -546,18 +526,6 @@ export function updateFromEvent(event) {
546526
break
547527
}
548528

549-
case 'slave-device-polling-update': {
550-
if (event.params.name in slaveDevices) {
551-
Object.assign(slaveDevices[event.params.name].attrs, event.params.attrs)
552-
slaveDevicesSetLocalStorageCacheDebouncer.call(slaveDevices)
553-
}
554-
else {
555-
logger.warn(`received slave-device-polling-update event for unknown device "${event.params.name}"`)
556-
}
557-
558-
break
559-
}
560-
561529
case 'slave-device-add': {
562530
if (event.params.name in slaveDevices) {
563531
logger.debug(`received slave-device-add event for already existing device "${event.params.name}"`)
@@ -579,10 +547,6 @@ export function updateFromEvent(event) {
579547
logger.warn(`received slave-device-remove event for unknown device "${event.params.name}"`)
580548
}
581549

582-
if (polledDeviceName === event.params.name) {
583-
polledDeviceName = null
584-
}
585-
586550
break
587551
}
588552

@@ -649,14 +613,6 @@ export function updateFromEvent(event) {
649613

650614
break
651615
}
652-
653-
case 'device-polling-update': {
654-
Object.assign(mainDevice, event.params)
655-
mainDeviceSetLocalStorageCacheDebouncer.call(mainDevice)
656-
657-
break
658-
}
659-
660616
}
661617
}
662618

@@ -900,96 +856,6 @@ export function reload(now = false) {
900856
}
901857
}
902858

903-
/**
904-
* Return the name of the polled device.
905-
* @alias qtoggle.cache.getPolledDeviceName
906-
* @returns {?String}
907-
*/
908-
export function getPolledDeviceName() {
909-
return polledDeviceName
910-
}
911-
912-
/**
913-
* Set the name of the polled device. Passing `null` disables polling.
914-
* @alias qtoggle.cache.setPolledDeviceName
915-
* @param {?String} [deviceName]
916-
*/
917-
export function setPolledDeviceName(deviceName) {
918-
if (deviceName == null) {
919-
logger.debug('disabling device polling')
920-
}
921-
else {
922-
logger.debug(`setting polled device name to "${deviceName}"`)
923-
}
924-
925-
polledDeviceName = deviceName
926-
}
927-
928-
function pollDevice() {
929-
/* Choose between polling main device or a slave device */
930-
let device = null
931-
if (polledDeviceName) {
932-
logger.debug(`polling device "${polledDeviceName}"`)
933-
device = slaveDevices[polledDeviceName]
934-
if (!device) {
935-
logger.debug('skipping polling for unknown device')
936-
return
937-
}
938-
939-
if (!device.enabled) {
940-
logger.debug('skipping polling for disabled device')
941-
return
942-
}
943-
944-
BaseAPI.setSlaveName(polledDeviceName)
945-
}
946-
else {
947-
logger.debug('polling main device')
948-
}
949-
950-
DevicesAPI.getDevice().then(function (attrs) {
951-
952-
asap(function () {
953-
if (device && polledDeviceName === device.name) {
954-
if (!ObjectUtils.deepEquals(device.attrs, attrs)) {
955-
let partialDevice = {name: device.name, attrs: {}}
956-
DEVICE_POLLED_ATTRIBUTES.forEach(function (name) {
957-
if (name in attrs) {
958-
partialDevice.attrs[name] = attrs[name]
959-
}
960-
})
961-
NotificationsAPI.fakeServerEvent('slave-device-polling-update', partialDevice)
962-
}
963-
}
964-
else if (polledDeviceName === '') {
965-
if (!ObjectUtils.deepEquals(mainDevice, attrs)) {
966-
let partialAttrs = {}
967-
DEVICE_POLLED_ATTRIBUTES.forEach(function (name) {
968-
if (name in attrs) {
969-
partialAttrs[name] = attrs[name]
970-
}
971-
})
972-
NotificationsAPI.fakeServerEvent('device-polling-update', partialAttrs)
973-
}
974-
}
975-
})
976-
977-
}).catch(function (e) {
978-
979-
if (polledDeviceName == null) {
980-
logger.debug('ignoring polling error after polling disabled')
981-
return
982-
}
983-
if ((e instanceof BaseAPI.APIError) && (e.code === 'no such device')) {
984-
logger.debug('ignoring error while polling removed device')
985-
return
986-
}
987-
988-
logger.errorStack('polling failed', e)
989-
990-
})
991-
}
992-
993859
/**
994860
* Initialize the cache subsystem.
995861
* @alias qtoggle.cache.init
@@ -1006,26 +872,4 @@ export function init() {
1006872
}
1007873

1008874
})
1009-
1010-
/* Start a polling timer */
1011-
setInterval(function () {
1012-
1013-
/* Don't poll unless cache is ready */
1014-
if (!whenCacheReady.isFulfilled()) {
1015-
return
1016-
}
1017-
1018-
/* Don't poll unless window currently active */
1019-
if (!Window.isActive()) {
1020-
return
1021-
}
1022-
1023-
/* Don't poll if polling device disabled */
1024-
if (polledDeviceName == null) {
1025-
return
1026-
}
1027-
1028-
pollDevice()
1029-
1030-
}, DEVICE_POLL_INTERVAL * 1000)
1031875
}

qtoggleserver/frontend/js/common/reboot-device-mixin.js

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,6 @@ const RebootDeviceMixin = Mixin((superclass = Object) => {
6767
this.setProgress()
6868
this._rebooting = true
6969

70-
/* Disable polling while rebooting */
71-
let polledDeviceName = Cache.getPolledDeviceName()
72-
if (polledDeviceName === deviceName) {
73-
Cache.setPolledDeviceName(null)
74-
}
75-
7670
if (!Cache.isMainDevice(deviceName)) {
7771
BaseAPI.setSlaveName(deviceName)
7872
}
@@ -107,11 +101,6 @@ const RebootDeviceMixin = Mixin((superclass = Object) => {
107101
this.clearProgress()
108102
this._rebooting = false
109103

110-
/* Restore polling */
111-
if (polledDeviceName === deviceName) {
112-
Cache.setPolledDeviceName(deviceName)
113-
}
114-
115104
}.bind(this))
116105
}
117106

qtoggleserver/frontend/js/devices/device-form.js

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -128,18 +128,6 @@ class DeviceForm extends mix(PageForm).with(
128128
this.updateUI(/* fieldChangeWarnings = */ false)
129129
}
130130

131-
onBecomeCurrent() {
132-
if (this._deviceRemoved) {
133-
return
134-
}
135-
136-
Cache.setPolledDeviceName(this.getDeviceName())
137-
}
138-
139-
onLeaveCurrent() {
140-
Cache.setPolledDeviceName(null)
141-
}
142-
143131
/**
144132
* Update the entire form (fields & values) from the corresponding device.
145133
*/
@@ -402,9 +390,6 @@ class DeviceForm extends mix(PageForm).with(
402390
/* Device renamed, remember new name for reopening */
403391
logger.debug(`device "${deviceName}" renamed to "${value}"`)
404392
Devices.setRenamedDeviceName(value)
405-
406-
/* Disable polling since it would soon poll an inexistent device name */
407-
Cache.setPolledDeviceName(null)
408393
}
409394

410395
if (this._fullAttrdefs[name].reconnect) {

qtoggleserver/frontend/js/devices/devices-section.js

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -85,14 +85,6 @@ class DevicesSection extends Section {
8585
break
8686
}
8787

88-
case 'slave-device-polling-update': {
89-
if (deviceForm && (deviceForm.getDeviceName() === event.params.name) && !deviceForm.isRebooting()) {
90-
deviceForm.updateUI(/* fieldChangeWarnings = */ false)
91-
}
92-
93-
break
94-
}
95-
9688
case 'slave-device-add': {
9789
this.devicesTable.updateUIASAP()
9890

0 commit comments

Comments
 (0)