"""Builds examples/home-pages.json: icons, backgrounds and dynamic pages for this home (import it in the web UI, System tab). python tools/make_example_pages.py examples/home-pages.json """ import json import sys W, H = 84, 19 def blank(): return [[0] * W for _ in range(H)] def put(img, art, x0, y0): for y, row in enumerate(art): for x, c in enumerate(row): if c == '#' and 0 <= x0 + x < W and 0 <= y0 + y < H: img[y0 + y][x0 + x] = 1 return img def to_hex(img): out = '' for y in range(H): for i in range(11): v = 0 for k in range(8): x = i * 8 + k if x < W and img[y][x]: v |= 0x80 >> k out += '%02x' % v return out CLOUD = [ "...........", "....###....", "..##.####..", ".#########.", "###########", "###########", ".#########.", ] WEATHER = { 'sunny': [ ".....#.....", ".#...#...#.", "..#.....#..", "....###....", "...#####...", "##.#####.##", "...#####...", "....###....", "..#.....#..", ".#...#...#.", ".....#.....", ], 'clear-night': [ "....###....", "..###......", ".###.......", ".##......#.", "###.......", "###........", "###........", ".###.......", ".####....#.", "..#######..", "....###....", ], 'partlycloudy': [ "..#..#.....", "....###....", ".#.#####...", "...####....", "#.##..###..", "..#########", ".##########", "###########", "###########", ".#########.", "...........", ], 'cloudy': ["...........", "..........."] + CLOUD + ["..........."] * 2, 'fog': [ "...........", "###########", "...........", ".##########", "...........", "#########..", "...........", "..#########", "...........", "#########..", "...........", ], 'rainy': CLOUD + [ "...........", ".#...#...#.", "#...#...#..", "...........", ], 'pouring': CLOUD + [ ".#.#.#.#.#.", "#.#.#.#.#..", ".#.#.#.#.#.", "#.#.#.#.#..", ], 'lightning': CLOUD[:6] + [ ".....##....", "....##.....", "...#####...", ".....##....", "....##.....", ], 'lightning-rainy': CLOUD[:6] + [ ".#...##..#.", "#...##..#..", "...#####...", ".#...##..#.", "#...##..#..", ], 'snowy': CLOUD + [ ".#...#...#.", "...#...#...", ".#...#...#.", "...#...#...", ], 'snowy-rainy': CLOUD + [ ".#...#...#.", "#...#...#..", "...#...#...", ".#...#...#.", ], 'hail': CLOUD + [ ".##..##..##", ".##..##..##", "...........", "..##..##...", ], 'windy': [ "...........", "......##...", "........#..", "#########..", "...........", "###########", "...........", "#######....", "........#..", "......##...", "...........", ], 'windy-variant': CLOUD + [ "...........", "#########..", "...........", "..#########", ], 'default': [ ".....#.....", "....#.#....", "....#.#....", "...#.#.#...", "...#.#.#...", "..#..#..#..", "..#..#..#..", ".#.......#.", ".#...#...#.", "#.........#", "###########", ], } WEATHER['exceptional'] = WEATHER['default'] BOLT = [ "....##.", "...##..", "..##...", ".#####.", "...##..", "..##...", ".##....", "##.....", ] SUN_SMALL = [ "....#....", ".#.....#.", "...###...", "..#####..", "#.#####.#", "..#####..", "...###...", ".#.....#.", "....#....", ] DROP = [ "...#...", "...#...", "..###..", "..###..", ".#####.", "#######", "#######", "#######", ".#####.", "..###..", ] HOUSE = [ ".....#.....", "....###....", "...##.##...", "..##...##..", ".##.....##.", "##.......##", ".#.......#.", ".#.##....#.", ".#.##.##.#.", ".#.##.##.#.", ".#########.", ] PRINTER = [ "###########", "#.........#", "#.#######.#", "#....#....#", "#....#....#", "#.........#", "#...###...#", "#..#####..#", "###########", "#.........#", "###########", ] CALENDAR = [ ".#.....#...", "###########", "#.........#", "###########", "#.#.#.#.#.#", "#.........#", "#.#.#.#.#.#", "#.........#", "#.#.#.#...#", "#.........#", "###########", ] TREE = [ ".....#.....", "....###....", ".....#.....", "....###....", "...#####...", "....###....", "...#####...", "..#######..", "...#####...", "..#######..", ".#########.", ".....#.....", "....###....", ] MOON = WEATHER['clear-night'] STARS = [(1, 2), (24, 1), (27, 7), (3, 9), (22, 4)] CORNER = ["#####", "#....", "#....", "#...."] def corners(): img = blank() put(img, CORNER, 0, 0) put(img, [r[::-1] for r in CORNER], W - 5, 0) put(img, CORNER[::-1], 0, H - 4) put(img, [r[::-1] for r in CORNER[::-1]], W - 5, H - 4) return img bitmaps = {} for state, art in WEATHER.items(): bitmaps['weer ' + state] = put(blank(), art, 0, 0) bitmaps['bg energie'] = put(put(blank(), BOLT, 1, 0), SUN_SMALL, 0, 10) bitmaps['bg printer'] = put(blank(), PRINTER, 0, 0) bitmaps['bg binnen'] = put(blank(), HOUSE, 0, 0) bitmaps['bg verbruik'] = put(put(blank(), DROP, 1, 0), BOLT, 1, 11) bitmaps['bg datum'] = put(blank(), CALENDAR, 0, 4) bitmaps['bg kerst'] = put(blank(), TREE, 0, 3) bitmaps['bg dag'] = put(blank(), WEATHER['sunny'], 9, 0) night = put(blank(), MOON, 9, 0) for x, y in STARS: night[y][x] = 1 bitmaps['bg nacht'] = night bitmaps['bg kader'] = corners() bitmaps['bg vol'] = [[1] * W for _ in range(H)] def el(type, x, y, font='Normal', **kw): e = {'type': type, 'x': x, 'y': y, 'align': 'left', 'overlay': 'top', 'font': font, 'format': 0, 'language': 'nl', 'decimals': -1, 'text': '', 'suffix': '', 'entity': '', 'attribute': '', 'factor': 1, 'scroll_width': 0, 'scroll_speed': 8, 'target': '2000-01-01T00:00'} e.update(kw) return e ENVOY = 'sensor.envoy_000000000000_' pages = { # 1. Weather clock: large clock, weather icon, outside humidity and temperature 'Weer': {'slots': [], 'elements': [ el('clock', 0, 0, 'Full height'), el('image', 55, 0, text='weer ', entity='weather.knmi_home'), el('ha', 84, 2, 'Small', align='right', entity='weather.knmi_home', attribute='humidity', suffix='%', decimals=0), el('ha', 84, 13, 'Small', align='right', entity='weather.knmi_home', attribute='temperature', suffix='°C', decimals=1), ]}, # 2. Sun by day, moon and stars at night, large clock and the date 'Dag en nacht': {'slots': [{'start': '07:00', 'bitmap': 'bg dag'}, {'start': '19:00', 'bitmap': 'bg nacht'}], 'elements': [ el('clock', 84, 0, 'Full height', align='right'), el('date', 0, 13, 'Small', format=1), ]}, # 3. Energy: net power now (negative = export), solar today, small clock 'Energie': {'slots': [{'start': '00:00', 'bitmap': 'bg energie'}], 'elements': [ el('ha', 10, 0, entity=ENVOY + 'current_net_power_consumption', suffix=' W', decimals=0), el('ha', 11, 11, entity=ENVOY + 'today_s_energy_production', suffix=' kWh', decimals=1, factor=0.001), el('clock', 84, 0, 'Small', align='right'), ]}, # 4. 3D printer: progress, time left, status message (scrolls), nozzle temperature '3D printer': {'slots': [{'start': '00:00', 'bitmap': 'bg printer'}], 'elements': [ el('ha', 14, 0, entity='sensor.mainsailos_progress', suffix='%', decimals=0), el('ha', 84, 0, align='right', entity='sensor.mainsailos_print_time_left', suffix='m', decimals=0, factor=1 / 60), el('ha', 14, 13, 'Small', entity='sensor.mainsailos_current_display_message', scroll_width=46, scroll_speed=6), el('ha', 84, 13, 'Small', align='right', entity='sensor.mainsailos_extruder_temperature', suffix='°', decimals=0), ]}, # 5. Indoor climate: room temperature, outside, CO2 and air quality 'Binnen': {'slots': [{'start': '00:00', 'bitmap': 'bg binnen'}], 'elements': [ el('ha', 14, 2, entity='climate.home', attribute='current_temperature', suffix='°', decimals=1), el('image', 49, 0, text='weer ', entity='weather.knmi_home'), el('ha', 84, 2, 'Small', align='right', entity='weather.knmi_home', attribute='temperature', suffix='°', decimals=1), el('ha', 0, 13, 'Small', entity='sensor.eco2', text='CO2 ', suffix='ppm', decimals=0), el('ha', 84, 13, 'Small', align='right', entity='sensor.air_quality_index_aqi_2', text='lucht '), ]}, # 6. Today: water and energy use, solar production 'Verbruik': {'slots': [{'start': '00:00', 'bitmap': 'bg verbruik'}], 'elements': [ el('ha', 10, 1, entity='sensor.util_water_usage_daily', suffix=' l', decimals=0, factor=1000), el('ha', 10, 11, entity=ENVOY + 'today_s_energy_consumption', suffix=' kWh', decimals=1, factor=0.001), el('clock', 84, 0, 'Small', align='right'), el('ha', 84, 13, 'Small', align='right', entity=ENVOY + 'today_s_energy_production', text='zon ', suffix='kWh', decimals=1, factor=0.001), ]}, # 7. Big clock in a frame; from 22:00 black digits on yellow 'Grote klok': {'slots': [{'start': '07:00', 'bitmap': 'bg kader'}, {'start': '22:00', 'bitmap': 'bg vol'}], 'elements': [ el('clock', 42, 0, 'Full height', align='center', overlay='invert'), ]}, # 8. The date in Dutch with a calendar 'Datum': {'slots': [{'start': '00:00', 'bitmap': 'bg datum'}], 'elements': [ el('date', 14, 1, format=5), el('date', 14, 12, 'Small', format=2), el('clock', 84, 12, 'Small', align='right'), ]}, # Countdown to Christmas 'Aftellen': {'slots': [{'start': '00:00', 'bitmap': 'bg kerst'}], 'elements': [ el('text', 14, 1, 'Small', text='Kerst over'), el('countdown', 14, 10, target='2026-12-25T00:00', suffix='Vrolijk kerst!', scroll_width=70, scroll_speed=8), ]}, # Stopwatch, large 'Stopwatch': {'slots': [], 'elements': [ el('stopwatch', 42, 0, 'Full height', align='center'), ]}, } backup = { 'format': 'flipdot-backup', 'version': 1, 'firmware': '2.5.0', 'exported': '2026-10-15T00:00:00Z', 'bitmaps': [{'name': n, 'data': to_hex(b)} for n, b in bitmaps.items()], 'pages': [{'name': n, 'page': p} for n, p in pages.items()], 'fonts': [], } json.dump(backup, open(sys.argv[1], 'w', encoding='utf-8'), ensure_ascii=False, indent=1) print(len(bitmaps), 'bitmaps,', len(pages), 'pages')