async def load_from_data(self, data: GenericJSONDict) -> None: attrs_start = ['enabled'] # These will be loaded first, in this order attrs_end = ['expression'] # These will be loaded last, in this order attr_items = data.items() attr_items = [a for a in attr_items if (a[0] not in attrs_start) and (a[0] not in attrs_end)] attr_items_start = [] for n in attrs_start: v = data.get(n) if v is not None: attr_items_start.append((n, v)) # Sort the rest of the attributes alphabetically attr_items.sort(key=lambda i: i[0]) attr_items_end = [] for n in attrs_end: v = data.get(n) if v is not None: attr_items_end.append((n, v)) attr_items = attr_items_start + attr_items + attr_items_end for name, value in attr_items: if name in ('id', 'value'): continue # Value is also among the persisted fields try: self.debug('loading %s = %s', name, json_utils.dumps(value)) await self.set_attr(name, value) except Exception as e: self.error('failed to set attribute %s = %s: %s', name, json_utils.dumps(value), e) # Value if await self.is_persisted() and data.get('value') is not None: self._value = data['value'] self.debug('loaded value = %s', json_utils.dumps(self._value)) if await self.is_writable(): # Write the just-loaded value to the port value = self._value if self._transform_write: value = await self.adapt_value_type(self._transform_write.eval()) await self.write_value(value) elif self.is_enabled(): try: value = await self.read_transformed_value() if value is not None: self._value = value self.debug('read value = %s', json_utils.dumps(self._value)) except Exception as e: self.error('failed to read value: %s', e, exc_info=True)
async def load_from_data(self, data: GenericJSONDict) -> None: attrs = data.get('attrs') if attrs: if 'value' in data: attrs['value'] = data['value'] self.update_cached_attrs(attrs) # Attributes that are kept on master for attr in ('tag', 'expression', 'last_sync', 'expires'): if attr in data: await self.set_attr(attr, data[attr]) self._provisioning = set(data.get('provisioning', []))
async def put_frontend(request: core_api.APIRequest, params: GenericJSONDict) -> None: # core_api.validate(panels, FRONTEND_SCHEMA) TODO: validate against schema logger.debug('restoring frontend configuration') await persist.remove('frontend_prefs') prefs = params.get('prefs') if prefs: for p in prefs: logger.debug('restoring frontend prefs for username "%s"', p.get('id')) await persist.insert('frontend_prefs', p) dashboard_panels = params.get('dashboard_panels', []) logger.debug('restoring dashboard panels') await persist.set_value('dashboard_panels', dashboard_panels) await core_events.trigger(DashboardUpdateEvent(request=request, panels=dashboard_panels))
async def add_slave_device_retry_disabled(properties: GenericJSONDict) -> slaves_devices.Slave: try: return await add_slave_device(properties) except core_api.APIError: if properties.get('enabled', True): core_api.logger.warning('adding device failed, adding it as disabled', exc_info=True) return await add_slave_device(dict(properties, enabled=False)) else: raise
async def load_from_data(self, data: GenericJSONDict) -> None: # Only consider locally persisted attributes for permanently offline devices. For online devices, we always use # fresh attributes received from device. attrs = data.get('attrs') if attrs and self._slave.is_permanently_offline(): if 'value' in data: attrs['value'] = data['value'] self.update_cached_attrs(attrs) # Attributes that are kept on master for attr in MASTER_ATTRS: if attr in data: await self.set_attr(attr, data[attr]) self._history_last_timestamp = data.get('history_last_timestamp', 0) self._provisioning = set(data.get('provisioning', [])) # Enable if enabled remotely await self.update_enabled()
async def add_virtual_port(attrs: GenericJSONDict) -> core_ports.BasePort: id_ = attrs['id'] type_ = attrs['type'] min_ = attrs.get('min') max_ = attrs.get('max') integer = attrs.get('integer') step = attrs.get('step') choices = attrs.get('choices') core_api.logger.debug('adding port "%s"', id_) if core_ports.get(id_): raise core_api.APIError(400, 'duplicate-port') if len(core_vports.all_port_args()) >= settings.core.virtual_ports: raise core_api.APIError(400, 'too-many-ports') await core_vports.add(id_, type_, min_, max_, integer, step, choices) port = await core_ports.load_one( 'qtoggleserver.core.vports.VirtualPort', { 'id_': id_, 'type_': type_, 'min_': min_, 'max_': max_, 'integer': integer, 'step': step, 'choices': choices }, trigger_add= False # Will trigger add event manually, later, after we've enabled the port ) # A virtual port is enabled by default await port.enable() await port.save() await port.trigger_add() return port
async def patch_firmware(request: core_api.APIRequest, params: GenericJSONDict) -> None: core_api_schema.validate(params, core_api_schema.PATCH_FIRMWARE) status = await fwupdate.get_status() if status not in (fwupdate.STATUS_IDLE, fwupdate.STATUS_ERROR): raise core_api.APIError(503, 'busy') if params.get('url'): await fwupdate.update_to_url(params['url']) else: # Assuming params['version'] await fwupdate.update_to_version(params['version'])
async def post_ports(request: core_api.APIRequest, params: GenericJSONDict) -> Attributes: core_api_schema.validate(params, core_api_schema.POST_PORTS) port_id = params['id'] port_type = params['type'] _min = params.get('min') _max = params.get('max') integer = params.get('integer') step = params.get('step') choices = params.get('choices') if core_ports.get(port_id): raise core_api.APIError(400, 'duplicate port') if len(core_vports.all_settings()) >= settings.core.virtual_ports: raise core_api.APIError(400, 'too many ports') core_vports.add(port_id, port_type, _min, _max, integer, step, choices) port = await core_ports.load_one( 'qtoggleserver.core.vports.VirtualPort', { 'port_id': port_id, '_type': port_type, '_min': _min, '_max': _max, 'integer': integer, 'step': step, 'choices': choices } ) # A virtual port is enabled by default await port.enable() await port.save() return await port.to_json()
async def post_reset(request: core_api.APIRequest, params: GenericJSONDict) -> None: core_api_schema.validate(params, core_api_schema.POST_RESET) factory = params.get('factory') if factory: core_api.logger.info('resetting to factory defaults') core_ports.reset() core_vports.reset() core_device.reset() if settings.webhooks.enabled: core_webhooks.reset() if settings.reverse.enabled: core_reverse.reset() if settings.slaves.enabled: slaves.reset() main.loop.call_later(2, system.reboot)
async def post_introspect(request: core_api.APIRequest, params: GenericJSONDict) -> GenericJSONDict: core_api_schema.validate(params, core_api_schema.POST_INTROSPECT) exc_str = None res_str = None try: imports = params.get('imports', []) extra_locals = {} for imp in imports: extra_locals[imp.split('.')[0]] = importlib.__import__(imp) result = eval(params['code'], globals(), dict(locals(), **extra_locals)) if inspect.isawaitable(result): result = await result res_str = str(result) except Exception: exc_str = traceback.format_exc() return {'result': res_str, 'exception': exc_str}
async def patch_slave_device(request: core_api.APIRequest, name: str, params: GenericJSONDict) -> None: core_api_schema.validate(params, api_schema.PATCH_SLAVE_DEVICE) slave = slaves_devices.get(name) if not slave: raise core_api.APIError(404, 'no such device') if params.get('enabled') is True and not slave.is_enabled(): await slave.enable() elif params.get('enabled') is False and slave.is_enabled(): await slave.disable() if params.get('poll_interval') and params.get('listen_enabled'): raise core_api.APIError(400, 'listening and polling') if params.get('poll_interval') is not None: slave.set_poll_interval(params['poll_interval']) if params.get('listen_enabled') is not None: if params['listen_enabled']: # We need to know if device supports listening; we therefore call GET /device before enabling it if slave.is_enabled(): try: attrs = await slave.api_call('GET', '/device') except Exception as e: raise exceptions.adapt_api_error(e) from e if 'listen' not in attrs['flags']: raise core_api.APIError(400, 'no listen support') slave.enable_listen() else: slave.disable_listen() slave.save() slave.trigger_update()