def _to_block(self, block: FirmwareBlock, find_sid=True) -> Block: block = Block(**block.dict(), id=None, serviceId=self._name) block.id = self._find_sid(block.nid, block.type) if find_sid else None resolve_data_ids(block.data, self._find_sid) # Special case, where the API data format differs from proto-ready format if block.type == const.SEQUENCE_BLOCK_TYPE: sequence.serialize(block) return block
async def test_to_block(app, client, store): store['alias', 123] = dict() store['4-2', 24] = dict() ctrl = controller.fget(app) assert ctrl._to_block(FirmwareBlock(nid=123, type='', data={})).id == 'alias' # Service ID not found: create placeholder generated = ctrl._to_block(FirmwareBlock(nid=456, type='', data={})) assert generated.id.startswith(const.GENERATED_ID_PREFIX)
async def clear_blocks(self) -> list[BlockIdentity]: """ Remove all user-created blocks on the controller. System blocks will not be removed, but the display settings will be reset. Returns: list[BlockIdentity]: The ids of all removed blocks. """ async with self._execute('Remove all blocks'): await self._cmder.clear_blocks() self._store.clear() await self._cmder.write_block( FirmwareBlock( nid=const.DISPLAY_SETTINGS_NID, type='DisplaySettings', data={}, )) ids = [ BlockIdentity(id=sid, nid=nid, serviceId=self._name) for (sid, nid) in block_cache.keys(self.app) if nid >= const.USER_NID_START ] block_cache.delete_all(self.app) return ids
async def _sync_time(self): now = datetime.now() ticks_block = await self.commander.write_block( FirmwareBlock(nid=const.SYSTIME_NID, type='Ticks', data={'secondsSinceEpoch': int(now.timestamp())})) ms = ticks_block.data['millisSinceBoot'] uptime = timedelta(milliseconds=ms) LOGGER.info(f'System uptime: {uptime}')
async def _to_block( self, payload: EncodedPayload, opts: codec.DecodeOpts, ) -> FirmwareBlock: enc_id = (payload.blockType, payload.subtype) enc_content = payload.content (blockType, subtype), data = await self._codec.decode(enc_id, enc_content, opts) return FirmwareBlock( nid=payload.blockId, type=join_type(blockType, subtype), data=data, )
def _to_firmware_block(self, block: Block, find_nid=True) -> FirmwareBlock: sid = block.id nid = block.nid if nid is None: try: nid = self._store.right_key(sid) if find_nid else 0 except KeyError: raise exceptions.UnknownId( f'No numeric ID matching [sid={block.id},type={block.type}] found in store' ) block = FirmwareBlock(nid=nid, type=block.type, data=block.data) # Special case, where the API data format differs from proto-ready format if block.type == const.SEQUENCE_BLOCK_TYPE: sequence.parse(block) resolve_data_ids(block.data, self._find_nid) return block
def default_blocks() -> dict[int, FirmwareBlock]: return { block.nid: block for block in [ FirmwareBlock( nid=const.SYSINFO_NID, type='SysInfo', data={'deviceId': 'FACADE'}, ), FirmwareBlock( nid=const.SYSTIME_NID, type='Ticks', data={ 'millisSinceBoot': 0, 'secondsSinceEpoch': 0, }, ), FirmwareBlock( nid=const.ONEWIREBUS_NID, type='OneWireBus', data={}, ), FirmwareBlock( nid=const.WIFI_SETTINGS_NID, type='WiFiSettings', data={}, ), FirmwareBlock( nid=const.TOUCH_SETTINGS_NID, type='TouchSettings', data={}, ), FirmwareBlock( nid=const.DISPLAY_SETTINGS_NID, type='DisplaySettings', data={'timeZone': 'Africa/Casablanca'}, ), FirmwareBlock( nid=const.SPARK_PINS_NID, type='Spark3Pins', data={ 'channels': [ { 'id': 1 }, { 'id': 2 }, { 'id': 3 }, { 'id': 4 }, { 'id': 5 }, ] }, ), ] }
async def write(self, request_b64: Union[str, bytes]): # pragma: no cover try: self.update_ticks() _, dict_content = await self._codec.decode( (codec.REQUEST_TYPE, None), request_b64, ) request = EncodedRequest(**dict_content) payload = request.payload if payload: (in_blockType, _), in_content = await self._codec.decode( (payload.blockType, payload.subtype), payload.content, ) else: in_blockType = 0 in_content = None response = EncodedResponse(msgId=request.msgId, error=ErrorCode.OK, payload=[]) if self.next_error: error = self.next_error.pop(0) if error is None: return # No response at all else: response.error = error elif request.opcode in [ Opcode.NONE, Opcode.VERSION, ]: await self.welcome() elif request.opcode in [Opcode.BLOCK_READ, Opcode.STORAGE_READ]: block = self._blocks.get(payload.blockId) if not block: response.error = ErrorCode.INVALID_BLOCK_ID else: (blockType, subtype), content = await self._codec.encode( (block.type, None), block.data, ) response.payload = [ EncodedPayload(blockId=block.nid, blockType=blockType, subtype=subtype, content=content) ] elif request.opcode in [ Opcode.BLOCK_READ_ALL, Opcode.STORAGE_READ_ALL, ]: for block in self._blocks.values(): (blockType, subtype), content = await self._codec.encode( (block.type, None), block.data, ) response.payload.append( EncodedPayload( blockId=block.nid, blockType=blockType, subtype=subtype, content=content, )) elif request.opcode == Opcode.BLOCK_WRITE: block = self._blocks.get(payload.blockId) if not block: response.error = ErrorCode.INVALID_BLOCK_ID elif not in_content: response.error = ErrorCode.INVALID_BLOCK elif in_blockType != block.type: response.error = ErrorCode.INVALID_BLOCK_TYPE else: block.data = in_content (blockType, subtype), data = await self._codec.encode( (block.type, None), block.data, ) response.payload = [ EncodedPayload(blockId=block.nid, blockType=blockType, subtype=subtype, data=data) ] elif request.opcode == Opcode.BLOCK_CREATE: nid = payload.blockId block = self._blocks.get(nid) if block: response.error = ErrorCode.BLOCK_NOT_CREATABLE elif nid > 0 and nid < const.USER_NID_START: response.error = ErrorCode.BLOCK_NOT_CREATABLE elif not in_content: response.error = ErrorCode.INVALID_BLOCK else: nid = nid or next(self._id_counter) block = FirmwareBlock( nid=nid, type=in_blockType, data=in_content, ) self._blocks[nid] = block (blockType, subtype), content = await self._codec.encode( (block.type, None), block.data, ) response.payload = [ EncodedPayload( blockId=block.nid, blockType=blockType, subtype=subtype, content=content, ) ] elif request.opcode == Opcode.BLOCK_DELETE: nid = payload.blockId block = self._blocks.get(nid) if not block: response.error = ErrorCode.INVALID_BLOCK_ID elif nid < const.USER_NID_START: response.error = ErrorCode.BLOCK_NOT_DELETABLE else: del self._blocks[nid] elif request.opcode == Opcode.BLOCK_DISCOVER: # Always return spark pins when discovering blocks block = self._blocks[const.SPARK_PINS_NID] (blockType, subtype), content = await self._codec.encode( (block.type, None), block.data, ) response.payload = [ EncodedPayload( blockId=block.nid, blockType=blockType, subtype=subtype, content=content, ) ] elif request.opcode == Opcode.REBOOT: self._start_time = datetime.now() self.update_ticks() elif request.opcode == Opcode.CLEAR_BLOCKS: self._blocks = default_blocks() self.update_ticks() elif request.opcode == Opcode.CLEAR_WIFI: self._blocks[const.WIFI_SETTINGS_NID].data.clear() elif request.opcode == Opcode.FACTORY_RESET: self._blocks = default_blocks() self.update_ticks() elif request.opcode == Opcode.FIRMWARE_UPDATE: pass else: response.error = ErrorCode.INVALID_OPCODE _, response_b64 = await self._codec.encode( (codec.RESPONSE_TYPE, None), response.dict()) await self._on_data(response_b64) except Exception as ex: LOGGER.error(strex(ex)) raise ex