"""The Elecrow CrowPanel 7.0" (app 0.2.93, firmware 0.2.79): a third board, the Guition's kind at 800x480 on 4 MB of flash.""" import re import sys import unittest from pathlib import Path ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(ROOT / 'tools')) import profiles # noqa: E402 sys.path.insert(0, str(ROOT / 'screen_manager/app')) import camera_feed # noqa: E402 import core # noqa: E402 sys.path.insert(0, str(ROOT / 'components/smart_display')) import partitions # noqa: E402 BOARD = profiles.BOARDS['elecrow'] TEXT = BOARD.read_text() SUBS = profiles.substitutions_of(BOARD) class BoardFile(unittest.TestCase): def test_it_is_a_landscape_guition(self): self.assertEqual((SUBS['DISPLAY_W'], SUBS['DISPLAY_H']), ('800', '480')) guition = profiles.substitutions_of(profiles.BOARDS['guition']) # The vertical sizes are the Guition's: the rows, the tile height, the fonts. for name in ('GRID_ROW1_Y', 'GRID_ROW2_Y', 'GRID_ROW3_Y', 'TILE_H', 'SCROLL_Y', 'SCROLL_H', 'HEADER_Y', 'FONT_LABEL_SIZE', 'FONT_ICON_SIZE', 'FONT_HEADLINE_SIZE', 'TILE_ICON_SIZE', 'PAGE_BAR_H'): self.assertEqual(SUBS[name], guition[name], name) # Two columns of tiles fill the width with the Guition's margins and gap. col1, col2, tile = int(SUBS['GRID_COL1_X']), int(SUBS['GRID_COL2_X']), int(SUBS['TILE_W']) self.assertEqual(col1, 16) self.assertEqual(col2, col1 + tile + 12) self.assertEqual(col2 + tile + 16, 800) self.assertEqual(int(SUBS['PAGE_KEY_W']), 400) self.assertEqual(int(SUBS['TOUCH_TEST_FAR_X']), 800 - 12 - 24 - 1) self.assertEqual(int(SUBS['TOUCH_TEST_MID_X']), 400 - 12) self.assertEqual(int(SUBS['SETTINGS_HOLD_W']), 800 - 2 * int(SUBS['EDGE_SWIPE_BAND_PX'])) def test_it_does_not_turn(self): self.assertEqual(SUBS['ROTATION_SUPPORTED'], 'false') self.assertEqual(SUBS['APPLY_ROTATION'], '') self.assertEqual(SUBS['APPLY_ROTATION_ENTITY'], '') self.assertNotIn('id: setting_rotation', TEXT) def test_the_hardware_of_the_crowpanel(self): self.assertIn('flash_size: 4MB', TEXT) self.assertIn('platform: mipi_rgb', TEXT) self.assertIn('model: RPI', TEXT) self.assertIn('pclk_inverted: true', TEXT) self.assertIn('red: [14, 21, 47, 48, 45]', TEXT) self.assertIn('green: [9, 46, 3, 8, 16, 1]', TEXT) self.assertIn('blue: [15, 7, 6, 5, 4]', TEXT) self.assertIn('sda: GPIO19', TEXT) self.assertIn('scl: GPIO20', TEXT) self.assertIn('scan: true', TEXT) self.assertIn('address: 0x5D', TEXT) self.assertIn('pin: GPIO2', TEXT) # The USB pins are the touch I2C: the header that frees them. self.assertIn('', TEXT) # The V3.0's PCA9557 brings the GT911 out of reset before the driver runs (firmware 0.2.82). self.assertIn(' elecrow_touch_reset:\n id: touch_reset\n i2c_id: touch_bus\n address: 0x18', TEXT) code = '\n'.join(line for line in TEXT.split('\n') if not line.lstrip().startswith('#')) self.assertNotIn('reset_pin', code) self.assertNotIn('interrupt_pin', code) header = (ROOT / 'components/smart_display/elecrow_touch_reset.h').read_text() # Elecrow's own values at setup, and the sequence in that order. self.assertIn('this->reset(0x00, 0x00, 0x01, 20, 100, 0x02)', header) order = [header.index(step) for step in ("write_reg_(0x03, config)", "write_reg_(0x01, low)", "delay(hold_ms)", "write_reg_(0x01, high)", "delay(wait_ms)", "write_reg_(0x03, final_config)")] self.assertEqual(order, sorted(order)) self.assertIn('return 700.0f', header) for action in ('touch_probe', 'touch_reset'): self.assertIn(f'- action: {action}', TEXT) self.assertIn('assertion_level: SILENT', TEXT) def test_the_flash_fits_by_three_measures(self): self.assertIn('partitions: 4mb_wide_slots', TEXT) self.assertIn('CONFIG_MBEDTLS_TLS_CLIENT_ONLY: y', TEXT) fonts = re.search(r'^font:\n(.*?)(?=^[a-z_]+:)', TEXT, re.M | re.S)[1] for font in ('materialdesign_icons', 'materialdesign_icons_mini', 'materialdesign_icons_big', 'watch_icon'): self.assertIn(f'- id: !extend {font}\n bpp: 2', fonts) self.assertNotIn('!extend headline', fonts) def test_the_usb_phy_header_runs_before_every_component(self): header = (ROOT / 'components/smart_display/elecrow_usb_phy.h').read_text() self.assertIn('__attribute__((constructor(101)))', header) self.assertIn('usb_serial_jtag_ll_phy_enable_pad(false)', header) for reg in ('IO_MUX_GPIO19_REG', 'IO_MUX_GPIO20_REG'): self.assertIn(reg, header) # ESPHome includes every header of the component in every build: the code is behind a flag only this board sets. self.assertIn('#if defined(ESP_SCREENS_FREE_USB_PADS) && ESP_SCREENS_FREE_USB_PADS', header) for board, path in profiles.BOARDS.items(): self.assertEqual('-DESP_SCREENS_FREE_USB_PADS=1' in path.read_text(), board == 'elecrow', board) def test_the_partition_table(self): table = partitions.TABLES['4mb_wide_slots'] rows = [[cell.strip() for cell in line.split(',')] for line in table.splitlines() if line.strip() and not line.startswith('#')] by_name = {row[0]: row for row in rows} self.assertEqual(by_name['app0'][1:], ['app', 'ota_0', '0x10000', '0x1F0000']) self.assertEqual(by_name['app1'][1:], ['app', 'ota_1', '0x200000', '0x1F0000']) self.assertEqual(int(by_name['nvs'][3], 16) + int(by_name['nvs'][4], 16), 4 * 1024 * 1024) # The slots touch neither each other nor the table before them. self.assertEqual(int(by_name['app0'][3], 16) + int(by_name['app0'][4], 16), int(by_name['app1'][3], 16)) self.assertEqual(int(by_name['app1'][3], 16) + int(by_name['app1'][4], 16), int(by_name['nvs'][3], 16)) self.assertGreaterEqual(int(by_name['app0'][3], 16), int(by_name['phy_init'][3], 16) + int(by_name['phy_init'][4], 16)) for name in ('app0', 'app1'): self.assertEqual(int(by_name[name][3], 16) % 0x10000, 0, f'{name} is 64 KB aligned') def test_the_entry_files(self): for name in ('elecrow-crowpanel7.yaml', 'packages/elecrow.yaml'): self.assertIn('boards/elecrow-crowpanel7.yaml', (ROOT / name).read_text(), name) self.assertIn('files: [packages/elecrow.yaml]', (ROOT / 'installers/elecrow.yaml').read_text()) self.assertEqual(profiles.board_of('packages/elecrow.yaml'), 'elecrow') class TheApp(unittest.TestCase): def test_the_board_is_read_from_the_type_sensor(self): registry = [] states = {} for device, value in (('d1', 'elecrow'), ('d2', 'guition'), ('d3', 'unavailable'), ('d4', None), ('d5', 'cyd')): registry += [{'entity_id': f'text.{device}_tiles', 'platform': 'esphome', 'original_name': 'Tile settings', 'device_id': device}, {'entity_id': f'sensor.{device}_type', 'platform': 'esphome', 'original_name': 'Guition screen type', 'device_id': device}] states[f'text.{device}_tiles'] = {'state': 'Synced'} if value is not None: states[f'sensor.{device}_type'] = {'state': value} registry.append({'entity_id': 'text.d6_tiles', 'platform': 'esphome', 'original_name': 'Tile settings', 'device_id': 'd6'}) states['text.d6_tiles'] = {'state': 'Synced'} screens = {s['device_id']: s['board'] for s in core.discover_screens(registry, states, [{'id': f'd{n}', 'name': f'D{n}'} for n in range(1, 7)], [])} # The value names the board; a sensor without a board's name (older firmware, a restart) is a Guition; no sensor is no large board. self.assertEqual(screens, {'d1': 'elecrow', 'd2': 'guition', 'd3': 'guition', 'd4': 'guition', 'd5': 'guition', 'd6': 'unknown'}) def test_the_app_knows_the_board(self): self.assertIn('elecrow', core.REFS) self.assertEqual(core.ALERT_LIMITS['elecrow'], {'title': int(SUBS['ALERT_TITLE_MAX']), 'subtitle': int(SUBS['ALERT_SUBTITLE_MAX']), 'button_text': int(SUBS['ALERT_BUTTON_MAX'])}) self.assertIn('files: [packages/elecrow.yaml]', core.installation_yaml({'board': 'elecrow', 'name': 'hall', 'friendly_name': 'Hall'})) def test_the_camera_boxes_are_the_profile_sizes(self): self.assertEqual(camera_feed.BOXES['elecrow'], {'full': (int(SUBS['CAMERA_FULL_W']), int(SUBS['CAMERA_FULL_H'])), 'thumb': (int(SUBS['CAMERA_THUMB_W']), int(SUBS['CAMERA_THUMB_H']))}) self.assertTrue(camera_feed.can_show({'board': 'elecrow', 'firmware': '0.2.79'})) self.assertTrue(camera_feed.can_show_live({'board': 'elecrow', 'firmware': '0.2.79'})) self.assertTrue(camera_feed.can_show_cover({'board': 'elecrow', 'firmware': '0.2.79'})) # The alert's picture fits the wider card. self.assertEqual(int(SUBS['CAMERA_THUMB_W']), int(SUBS['ALERT_CARD_W']) - 2 * int(SUBS['ALERT_IMAGE_INSET'])) def test_the_editor_offers_the_board(self): installer = (ROOT / 'web/src/components/InstallerView.vue').read_text() self.assertIn('value="elecrow"', installer) for path in sorted((ROOT / 'screen_manager/translations').glob('*.json')): if path.stem != 'en-GB': self.assertIn('"board_elecrow"', path.read_text(), path.name) store = (ROOT / 'web/src/store.ts').read_text() self.assertIn('LARGE_BOARDS = ["guition", "elecrow"]', store) self.assertIn('elecrow: { width: 768', (ROOT / 'web/src/model/topbar.ts').read_text()) if __name__ == '__main__': unittest.main()