d6e5b35dcf
A tap on a cover now opens a card like Home Assistant's own, in the style of the climate and vacuum cards: a tall position slider on which the blind hangs from the top, a tilt slider over slats for venetian blinds, and open, stop and close keys. The key of the direction the cover moves is filled; a key that cannot move it further is disabled and stays out of the keys the card's tick enables. Sliders show their value while dragged and send it on release. The card offers only what supported_features allows. A battery-powered blind shows its battery at the top right: the app sends the device's battery sensor as the extra the vacuum card uses, and adds current_tilt_position to the attributes. Checking Studio 1 after its update to firmware 0.2.49 found that a setting number equal to its initial state 0 (night brightness 0 %) was never published, so Home Assistant kept it unknown and ESP Screens could not change it; apply_screen_settings now publishes a number without a state. The app also took "unavailable" for the device name during the restart and wrote sensor.esp_screens_unavailable; diagnostics ignore unknown and unavailable. The editor redraws its tiles, top bar and entity list when the live stream opened a screen before the first full inventory (a busy add-on), and loads app.js and style.css by content stamp after Safari kept an old script. Checked with 254 Python and 14 C++ tests, generate_packages --check, ESP32 builds of both profiles, and 20 API checks of the card on host builds of both boards. Firmware 0.2.50. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
44 lines
2.1 KiB
Python
44 lines
2.1 KiB
Python
"""The page loads its own script and styles by content stamp (app 0.2.58).
|
|
|
|
After updating to 0.2.57, Safari showed an empty Screen settings panel: it kept a cached script from before
|
|
the update. The page now asks for app.js and style.css with a stamp of their content, so a new version is
|
|
always fetched and an old one is never combined with a new page.
|
|
"""
|
|
import hashlib
|
|
import importlib.util
|
|
import tempfile
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
STATIC = ROOT / 'screen_manager/app/static'
|
|
HAS_AIOHTTP = importlib.util.find_spec('aiohttp') is not None
|
|
|
|
|
|
@unittest.skipUnless(HAS_AIOHTTP, 'Run using .venv-portal/bin/python for server tests')
|
|
class Assets(unittest.IsolatedAsyncioTestCase):
|
|
async def test_script_and_styles_carry_a_stamp_of_their_content(self):
|
|
import sys
|
|
sys.path.insert(0, str(ROOT / 'screen_manager/app'))
|
|
sys.path.insert(0, str(ROOT / 'tests'))
|
|
import test_scaling
|
|
from aiohttp.test_utils import TestClient, TestServer
|
|
from server import Manager, create_app
|
|
stamp = hashlib.sha1((STATIC / 'app.js').read_bytes() + (STATIC / 'style.css').read_bytes()).hexdigest()[:12]
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
manager = Manager(test_scaling.fake_ha(), Path(tmp) / 'screens.json')
|
|
async with TestClient(TestServer(create_app(manager, True))) as client:
|
|
response = await client.get('/')
|
|
page = await response.text()
|
|
self.assertEqual(response.status, 200)
|
|
self.assertIn(f'<script src="static/app.js?v={stamp}" defer></script>', page)
|
|
self.assertIn(f'<link rel="stylesheet" href="static/style.css?v={stamp}" />', page)
|
|
self.assertEqual(response.headers['Cache-Control'], 'no-store')
|
|
self.assertIn('text/html', response.headers['Content-Type'])
|
|
script = await client.get(f'/static/app.js?v={stamp}')
|
|
self.assertEqual((script.status, await script.read()), (200, (STATIC / 'app.js').read_bytes()))
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|