async def slave_device_forward( request: core_api.APIRequest, name: str, path: str, params: Optional[GenericJSONDict] = None, internal_use: bool = False ) -> Any: slave = slaves_devices.get(name) if not slave: raise core_api.APIError(404, 'no-such-device') if not internal_use: if not path.startswith('/'): path = '/' + path if path.startswith('/listen'): raise core_api.APIError(404, 'no-such-function') intercepted, response = await slave.intercept_request(request.method, path, params, request) if intercepted: return response override_disabled = request.query.get('override_disabled') if not slave.is_enabled() and (override_disabled != 'true'): raise core_api.APIError(404, 'device-disabled') override_offline = request.query.get('override_offline') if (not slave.is_online() or not slave.is_ready()) and (override_offline != 'true'): raise core_api.APIError(503, 'device-offline') timeout = request.query.get('timeout') if timeout is not None: try: timeout = int(timeout) except ValueError: raise core_api.APIError(400, 'invalid-field', field='timeout') from None else: # Use default slave timeout unless API call requires longer timeout timeout = settings.slaves.timeout for m, path_re in _LONG_TIMEOUT_API_CALLS: if request.method == m and path_re.fullmatch(path): timeout = settings.slaves.long_timeout break try: response = await slave.api_call(request.method, path, params, timeout=timeout, retry_counter=None) except Exception as e: raise slaves_exceptions.adapt_api_error(e) from e return response
async def post_slave_device_events(request: core_api.APIRequest, name: str, params: GenericJSONDict) -> None: slave = slaves_devices.get(name) if not slave: raise core_api.APIError(404, 'no-such-device') # Slave events endpoint has special privilege requirements: its token signature must be validated using slave admin # password auth = request.headers.get('Authorization') if not auth: slave.warning('missing authorization header') raise core_api.APIError(401, 'authentication-required') try: core_api_auth.parse_auth_header( auth, core_api_auth.ORIGIN_DEVICE, lambda u: slave.get_admin_password_hash(), require_usr=False ) except core_api_auth.AuthError as e: slave.warning(str(e)) raise core_api.APIError(401, 'authentication-required') from e core_api_schema.validate(params, api_schema.POST_SLAVE_DEVICE_EVENTS) if slave.get_poll_interval() > 0: raise core_api.APIError(400, 'polling-enabled') if slave.is_listen_enabled(): raise core_api.APIError(400, 'listening-enabled') # At this point we can be sure the slave is permanently offline try: await slave.handle_event(params) except Exception as e: raise core_api.APIError(500, 'unexpected-error', message=str(e)) from e slave.update_last_sync() await slave.save() # As soon as we receive event requests (normally generated by a webhook), we can consider the device is temporarily # reachable, so we apply provisioning values and update data locally slave.schedule_provisioning_and_update(1)
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()
async def delete_slave_device(request: core_api.APIRequest, name: str) -> None: slave = slaves_devices.get(name) if not slave: raise core_api.APIError(404, 'no-such-device') await slaves_devices.remove(slave)