Page 2 of 2
Re: Custom icons do not shown anymore / custom icons upload fails
Posted: Tuesday 10 June 2025 15:25
by frank666
Same problem domoticz2025.1 (Build 16675) in container, I tried to import icons created with https://domoticz- icon.aurelien-ve.fr but it gives me error :
error in loading the icon set: icon file: tower.png is to small or issue with extraction.
Re: Custom icons do not shown anymore / custom icons upload fails
Posted: Wednesday 11 June 2025 8:25
by gizmocuz
RonkA wrote: ↑Tuesday 10 June 2025 13:30
The icon in question is a custom icon i created called 'domoticz_custom_icon_Inverter.zip' ,a picture of my Solar-Edge inverter, but the icon that's being shown is not mine anymore. It looks like some sort of Victron gizmo..
Ahh, sorry about that, but I added an 'Inverter' icon a long time ago... I think your Icons had the same name (Inveter, Inverter48_On/Off)
It's an Inverter, does it really matter what brand it is? You got a much better one now
All custom icons are stored inside the database.
They are reloaded/stored on disk at Domoticz startup
It will check if the file exists, and if it does, it will not overwrite it
It will probably also find that the file exists when it is 0 bytes
I have changed this in beta xx, it will not try to overwrite 0 length files
When uploading a new custom image
But this is not an error, but for you an unfortunate event... You can create a new custom icon with the names SolarEdge
Re: Custom icons do not shown anymore / custom icons upload fails
Posted: Wednesday 11 June 2025 8:28
by gizmocuz
frank666 wrote: ↑Tuesday 10 June 2025 15:25
Same problem domoticz2025.1 (Build 16675) in container, I tried to import icons created with https://domoticz- icon.aurelien-ve.fr but it gives me error :
error in loading the icon set: icon file: tower.png is to small or issue with extraction.
That's great, but you really have to attach the zip file with the custom icon so we can have a look at it...
It is always easy to say it's a bug
Try it with this nice flour.... No issues here at all, also running Domoticz inside a docker compose container
Re: Custom icons do not shown anymore / custom icons upload fails
Posted: Wednesday 11 June 2025 16:32
by frank666
gizmo thanks for the example zip, I have now succeeded; the problem is https://domoticz- icon.aurelien-ve.fr there seems to be some problem as it creates a file in the zip with the extension 0 .
I enclose the files the first constructed manually
the second built via the site
result :

- image_2025-06-11_163841412.png (62.68 KiB) Viewed 344 times



Re: Custom icons do not shown anymore / custom icons upload fails
Posted: Wednesday 11 June 2025 20:48
by RonkA
Here is a python-script that generates the right icons from the generated ones by domoticz- icon.aurelien-ve.fr:
Code: Select all
import sys
import os
import zipfile
from PIL import Image
from io import BytesIO
def crop_icon(image_data):
with Image.open(BytesIO(image_data)) as img:
if img.size == (50, 50):
cropped = img.crop((1, 1, 49, 49)) # Remove 1 pixel from each edge
output = BytesIO()
cropped.save(output, format="PNG")
return output.getvalue(), cropped
return image_data, img.copy()
def resize_to_16x16(image: Image.Image):
resized = image.resize((16, 16), Image.LANCZOS)
output = BytesIO()
resized.save(output, format="PNG")
return output.getvalue()
def process_zip(zip_path):
zip_dir = os.path.dirname(zip_path)
zip_name = os.path.basename(zip_path)
new_zip_path = os.path.join(zip_dir, f"processed_{zip_name}")
icons_txt = None
cropped_icons = {}
resized_icons = {}
with zipfile.ZipFile(zip_path, 'r') as zin:
for item in zin.infolist():
data = zin.read(item.filename)
# Keep only icons.txt
if item.filename.lower() == "icons.txt":
icons_txt = (item.filename, data)
# Process only 48_Off and 48_On icons
elif item.filename.endswith("48_Off.png") or item.filename.endswith("48_On.png"):
cropped_data, cropped_img = crop_icon(data)
cropped_icons[item.filename] = cropped_data
# Create 16x16 icon from *_48_On.png only
if item.filename.endswith("48_On.png"):
base_name = item.filename.replace("48_On", "")
resized_data = resize_to_16x16(cropped_img)
resized_icons[base_name] = resized_data
# Create the new ZIP file
with zipfile.ZipFile(new_zip_path, 'w') as zout:
if icons_txt:
zout.writestr(icons_txt[0], icons_txt[1])
for filename, data in cropped_icons.items():
zout.writestr(filename, data)
for filename, data in resized_icons.items():
zout.writestr(filename, data)
print(f"Processed ZIP file saved as: {new_zip_path}")
if __name__ == '__main__':
if len(sys.argv) != 2:
print("Usage: python Iconcrop.py <file.zip>")
sys.exit(1)
zip_file_path = sys.argv[1]
if not os.path.isfile(zip_file_path) or not zip_file_path.endswith(".zip"):
print("Please provide a valid .zip file.")
sys.exit(1)
process_zip(zip_file_path)
save code as Iconcrop.py
I copied all custom icon zipfiles from the download-folder from windows into a new folder, also placed the python script and from commandprompt do for example
Code: Select all
C:\Users\Ron\domoticz icons>python Iconcrop.py domoticz_custom_icon_Kliko.zip
the reply should be:
Code: Select all
Nieuw ZIP-file created as: processed_domoticz_custom_icon_Kliko.zip
Re: Custom icons do not shown anymore / custom icons upload fails
Posted: Wednesday 11 June 2025 20:54
by frank666
tnx Ronka

Code: Select all
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Domoticz Icon Generator</title>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jszip.min.js"></script>
<script src="https://cdn.tailwindcss.com"></script>
<style>
body {
background: #f3f4f6; /* Soft light gray (grigio tenue) */
}
.container {
transition: transform 0.3s ease-in-out;
}
.container:hover {
transform: scale(1.02);
}
.glow {
box-shadow: 0 0 10px rgba(34, 197, 94, 0.5);
}
</style>
</head>
<body class="flex flex-col items-center justify-center min-h-screen">
<div class="container bg-white p-10 rounded-2xl shadow-md w-full max-w-lg mx-4">
<h1 class="text-3xl font-extrabold text-center text-indigo-700 mb-6">Domoticz Icon Generator</h1>
<div class="mb-6">
<label class="block text-sm font-medium text-gray-900 mb-2">Upload Icon (Any PNG)</label>
<input type="file" id="iconInput" accept="image/png" class="block w-full text-sm text-gray-900 file:mr-4 file:py-3 file:px-5 file:rounded-lg file:border-0 file:text-sm file:font-semibold file:bg-indigo-50 file:text-indigo-700 hover:file:bg-indigo-100 transition duration-200">
</div>
<button id="generateZip" class="glow w-full py-3 px-4 bg-green-500 text-white text-lg font-semibold rounded-lg hover:bg-green-600 focus:ring-4 focus:ring-green-300 transition duration-200">Generate ZIP</button>
<p id="status" class="mt-4 text-sm text-gray-900 text-center"></p>
</div>
<script>
const iconInput = document.getElementById('iconInput');
const generateZip = document.getElementById('generateZip');
const status = document.getElementById('status');
// Image processing functions
function resizeTo48x48(img, callback) {
const canvas = document.createElement('canvas');
canvas.width = 48;
canvas.height = 48;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, 48, 48);
canvas.toBlob(callback, 'image/png');
}
function cropIcon(img, callback) {
const canvas = document.createElement('canvas');
canvas.width = 48;
canvas.height = 48;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 1, 1, 48, 48, 0, 0, 48, 48);
canvas.toBlob(callback, 'image/png');
}
function resizeTo16x16(img, callback) {
const canvas = document.createElement('canvas');
canvas.width = 16;
canvas.height = 16;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, 16, 16);
canvas.toBlob(callback, 'image/png');
}
function createOffIcon(img, callback) {
const canvas = document.createElement('canvas');
canvas.width = 48;
canvas.height = 48;
const ctx = canvas.getContext('2d');
ctx.filter = 'grayscale(100%) opacity(50%)';
ctx.drawImage(img, 0, 0, 48, 48);
canvas.toBlob(callback, 'image/png');
}
// ZIP generation
generateZip.addEventListener('click', async () => {
if (!iconInput.files[0]) {
status.textContent = 'Please upload a PNG file.';
return;
}
status.textContent = 'Processing...';
const zip = new JSZip();
// Determine icon name
let iconName = iconInput.files[0].name.replace(/\.[^/.]+$/, '');
const iconNameCapitalized = iconName.charAt(0).toUpperCase() + iconName.slice(1);
let img = new Image();
let source = URL.createObjectURL(iconInput.files[0]);
img.onload = async () => {
// Resize input image to 48x48
const resizedBlob = await new Promise(resolve => resizeTo48x48(img, resolve));
const resizedImg = new Image();
resizedImg.src = URL.createObjectURL(resizedBlob);
resizedImg.onload = async () => {
// Crop the "On" icon
const croppedOnBlob = await new Promise(resolve => cropIcon(resizedImg, resolve));
zip.file(`${iconName}48_On.png`, croppedOnBlob);
// Create and crop the "Off" icon
const offImg = await new Promise(resolve => {
createOffIcon(resizedImg, blob => {
const offImg = new Image();
offImg.onload = () => resolve(offImg);
offImg.src = URL.createObjectURL(blob);
});
});
const croppedOffBlob = await new Promise(resolve => cropIcon(offImg, resolve));
zip.file(`${iconName}48_Off.png`, croppedOffBlob);
// Resize cropped "On" icon to 16x16
const croppedOnImg = new Image();
croppedOnImg.src = URL.createObjectURL(croppedOnBlob);
croppedOnImg.onload = async () => {
const resizedBlob = await new Promise(resolve => resizeTo16x16(croppedOnImg, resolve));
zip.file(`${iconName}.png`, resizedBlob);
// Add icons.txt with name;Name;Name format
zip.file('icons.txt', `${iconName};${iconNameCapitalized};${iconNameCapitalized}`);
// Generate and download ZIP
const content = await zip.generateAsync({ type: 'blob' });
const link = document.createElement('a');
link.href = URL.createObjectURL(content);
link.download = `${iconName}_icons.zip`;
link.click();
status.textContent = 'ZIP file downloaded!';
};
};
};
img.onerror = () => status.textContent = 'Error loading image.';
img.src = source;
});
</script>
</body>
</html>

- image_2025-06-12_165737833.png (17.64 KiB) Viewed 264 times
I made this , it is to be saved in .html and used on a server works well .
Re: Custom icons do not shown anymore / custom icons upload fails
Posted: Saturday 14 June 2025 14:16
by gizmocuz
Thanks for the feedback. Maybe you could let the author of this tool know that it sometimes generates icons with 0 byte length
Re: Custom icons do not shown anymore / custom icons upload fails
Posted: Saturday 14 June 2025 14:44
by HvdW
Which reminds me to say thank you to the Domoticz developers for the new icons in the latest release.
Re: Custom icons do not shown anymore / custom icons upload fails
Posted: Saturday 14 June 2025 15:49
by RonkA
Maybe you could let the author of this tool know that it sometimes generates icons with 0 byte length
I couldn't reach him, i don't have twitter/X, his GitHub is 404, and i don't have LinkedIn..
I made this , it is to be saved in .html and used on a server works well
Also Nice, saves a lot of time making new icons!!
Re: Custom icons do not shown anymore / custom icons upload fails
Posted: Tuesday 17 June 2025 13:22
by Filip
frank666 wrote: ↑Wednesday 11 June 2025 20:54
tnx Ronka

Code: Select all
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Domoticz Icon Generator</title>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jszip.min.js"></script>
<script src="https://cdn.tailwindcss.com"></script>
<style>
body {
background: #f3f4f6; /* Soft light gray (grigio tenue) */
}
.container {
transition: transform 0.3s ease-in-out;
}
.container:hover {
transform: scale(1.02);
}
.glow {
box-shadow: 0 0 10px rgba(34, 197, 94, 0.5);
}
</style>
</head>
<body class="flex flex-col items-center justify-center min-h-screen">
<div class="container bg-white p-10 rounded-2xl shadow-md w-full max-w-lg mx-4">
<h1 class="text-3xl font-extrabold text-center text-indigo-700 mb-6">Domoticz Icon Generator</h1>
<div class="mb-6">
<label class="block text-sm font-medium text-gray-900 mb-2">Upload Icon (Any PNG)</label>
<input type="file" id="iconInput" accept="image/png" class="block w-full text-sm text-gray-900 file:mr-4 file:py-3 file:px-5 file:rounded-lg file:border-0 file:text-sm file:font-semibold file:bg-indigo-50 file:text-indigo-700 hover:file:bg-indigo-100 transition duration-200">
</div>
<button id="generateZip" class="glow w-full py-3 px-4 bg-green-500 text-white text-lg font-semibold rounded-lg hover:bg-green-600 focus:ring-4 focus:ring-green-300 transition duration-200">Generate ZIP</button>
<p id="status" class="mt-4 text-sm text-gray-900 text-center"></p>
</div>
<script>
const iconInput = document.getElementById('iconInput');
const generateZip = document.getElementById('generateZip');
const status = document.getElementById('status');
// Image processing functions
function resizeTo48x48(img, callback) {
const canvas = document.createElement('canvas');
canvas.width = 48;
canvas.height = 48;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, 48, 48);
canvas.toBlob(callback, 'image/png');
}
function cropIcon(img, callback) {
const canvas = document.createElement('canvas');
canvas.width = 48;
canvas.height = 48;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 1, 1, 48, 48, 0, 0, 48, 48);
canvas.toBlob(callback, 'image/png');
}
function resizeTo16x16(img, callback) {
const canvas = document.createElement('canvas');
canvas.width = 16;
canvas.height = 16;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, 16, 16);
canvas.toBlob(callback, 'image/png');
}
function createOffIcon(img, callback) {
const canvas = document.createElement('canvas');
canvas.width = 48;
canvas.height = 48;
const ctx = canvas.getContext('2d');
ctx.filter = 'grayscale(100%) opacity(50%)';
ctx.drawImage(img, 0, 0, 48, 48);
canvas.toBlob(callback, 'image/png');
}
// ZIP generation
generateZip.addEventListener('click', async () => {
if (!iconInput.files[0]) {
status.textContent = 'Please upload a PNG file.';
return;
}
status.textContent = 'Processing...';
const zip = new JSZip();
// Determine icon name
let iconName = iconInput.files[0].name.replace(/\.[^/.]+$/, '');
const iconNameCapitalized = iconName.charAt(0).toUpperCase() + iconName.slice(1);
let img = new Image();
let source = URL.createObjectURL(iconInput.files[0]);
img.onload = async () => {
// Resize input image to 48x48
const resizedBlob = await new Promise(resolve => resizeTo48x48(img, resolve));
const resizedImg = new Image();
resizedImg.src = URL.createObjectURL(resizedBlob);
resizedImg.onload = async () => {
// Crop the "On" icon
const croppedOnBlob = await new Promise(resolve => cropIcon(resizedImg, resolve));
zip.file(`${iconName}48_On.png`, croppedOnBlob);
// Create and crop the "Off" icon
const offImg = await new Promise(resolve => {
createOffIcon(resizedImg, blob => {
const offImg = new Image();
offImg.onload = () => resolve(offImg);
offImg.src = URL.createObjectURL(blob);
});
});
const croppedOffBlob = await new Promise(resolve => cropIcon(offImg, resolve));
zip.file(`${iconName}48_Off.png`, croppedOffBlob);
// Resize cropped "On" icon to 16x16
const croppedOnImg = new Image();
croppedOnImg.src = URL.createObjectURL(croppedOnBlob);
croppedOnImg.onload = async () => {
const resizedBlob = await new Promise(resolve => resizeTo16x16(croppedOnImg, resolve));
zip.file(`${iconName}.png`, resizedBlob);
// Add icons.txt with name;Name;Name format
zip.file('icons.txt', `${iconName};${iconNameCapitalized};${iconNameCapitalized}`);
// Generate and download ZIP
const content = await zip.generateAsync({ type: 'blob' });
const link = document.createElement('a');
link.href = URL.createObjectURL(content);
link.download = `${iconName}_icons.zip`;
link.click();
status.textContent = 'ZIP file downloaded!';
};
};
};
img.onerror = () => status.textContent = 'Error loading image.';
img.src = source;
});
</script>
</body>
</html>
image_2025-06-12_165737833.png
I made this , it is to be saved in .html and used on a server works well .
I like this tool? Just some suggestions: I would prefer the possibility to upload 2 different files; one for ON and one for OFF. Because sometime the OFF icon is not just a "gray" version of the ON one...
And would be good to have it somewhere in the domoticz git...
Thanks!
Re: Custom icons do not shown anymore / custom icons upload fails
Posted: Thursday 19 June 2025 8:40
by frank666
@Filip I have modified as suggested here :
Code: Select all
<script type="text/javascript">
var gk_isXlsx = false;
var gk_xlsxFileLookup = {};
var gk_fileData = {};
function filledCell(cell) {
return cell !== '' && cell != null;
}
function loadFileData(filename) {
if (gk_isXlsx && gk_xlsxFileLookup[filename]) {
try {
var workbook = XLSX.read(gk_fileData[filename], { type: 'base64' });
var firstSheetName = workbook.SheetNames[0];
var worksheet = workbook.Sheets[firstSheetName];
// Convert sheet to JSON to filter blank rows
var jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1, blankrows: false, defval: '' });
// Filter out blank rows (rows where all cells are empty, null, or undefined)
var filteredData = jsonData.filter(row => row.some(filledCell));
// Heuristic to find the header row by ignoring rows with fewer filled cells than the next row
var headerRowIndex = filteredData.findIndex((row, index) =>
row.filter(filledCell).length >= filteredData[index + 1]?.filter(filledCell).length
);
// Fallback
if (headerRowIndex === -1 || headerRowIndex > 25) {
headerRowIndex = 0;
}
// Convert filtered JSON back to CSV
var csv = XLSX.utils.aoa_to_sheet(filteredData.slice(headerRowIndex)); // Create a new sheet from filtered array of arrays
csv = XLSX.utils.sheet_to_csv(csv, { header: 1 });
return csv;
} catch (e) {
console.error(e);
return "";
}
}
return gk_fileData[filename] || "";
}
</script><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Domoticz Icon Generator</title>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jszip.min.js"></script>
<script src="https://cdn.tailwindcss.com"></script>
<style>
body {
background: #1f2937; /* Dark gray background */
}
.container {
transition: transform 0.3s ease-in-out;
}
.container:hover {
transform: scale(1.02);
}
.glow {
box-shadow: 0 0 10px rgba(34, 197, 94, 0.5);
}
</style>
</head>
<body class="flex flex-col items-center justify-center min-h-screen">
<div class="container bg-gray-300 p-10 rounded-2xl shadow-md w-full max-w-lg mx-4">
<h1 class="text-3xl font-extrabold text-center text-indigo-700 mb-6">Domoticz Icon Generator</h1>
<div class="mb-6">
<label class="block text-sm font-medium text-gray-900 mb-2">Upload On Icon (PNG)</label>
<input type="file" id="onIconInput" accept="image/png" class="block w-full text-sm text-gray-900 file:mr-4 file:py-3 file:px-5 file:rounded-lg file:border-0 file:text-sm file:font-semibold file:bg-indigo-50 file:text-indigo-700 hover:file:bg-indigo-100 transition duration-200">
</div>
<div class="mb-6">
<label class="block text-sm font-medium text-gray-900 mb-2">Upload Off Icon (PNG)</label>
<input type="file" id="offIconInput" accept="image/png" class="block w-full text-sm text-gray-900 file:mr-4 file:py-3 file:px-5 file:rounded-lg file:border-0 file:text-sm file:font-semibold file:bg-indigo-50 file:text-indigo-700 hover:file:bg-indigo-100 transition duration-200">
</div>
<button id="generateZip" class="glow w-full py-3 px-4 bg-green-500 text-white text-lg font-semibold rounded-lg hover:bg-green-600 focus:ring-4 focus:ring-green-300 transition duration-200">Generate ZIP</button>
<p id="status" class="mt-4 text-sm text-gray-900 text-center"></p>
</div>
<script>
const onIconInput = document.getElementById('onIconInput');
const offIconInput = document.getElementById('offIconInput');
const generateZip = document.getElementById('generateZip');
const status = document.getElementById('status');
// Image processing functions
function resizeTo48x48(img, callback) {
const canvas = document.createElement('canvas');
canvas.width = 48;
canvas.height = 48;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, 48, 48);
canvas.toBlob(callback, 'image/png');
}
function cropIcon(img, callback) {
const canvas = document.createElement('canvas');
canvas.width = 48;
canvas.height = 48;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 1, 1, 48, 48, 0, 0, 48, 48);
canvas.toBlob(callback, 'image/png');
}
function resizeTo16x16(img, callback) {
const canvas = document.createElement('canvas');
canvas.width = 16;
canvas.height = 16;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, 16, 16);
canvas.toBlob(callback, 'image/png');
}
// ZIP generation
generateZip.addEventListener('click', async () => {
if (!onIconInput.files[0] || !offIconInput.files[0]) {
status.textContent = 'Please upload both On and Off PNG files.';
return;
}
status.textContent = 'Processing...';
const zip = new JSZip();
// Determine icon name from On icon
let iconName = onIconInput.files[0].name.replace(/\.[^/.]+$/, '');
const iconNameCapitalized = iconName.charAt(0).toUpperCase() + iconName.slice(1);
// Process On icon
let onImg = new Image();
onImg.src = URL.createObjectURL(onIconInput.files[0]);
onImg.onload = async () => {
// Resize On image to 48x48
const resizedOnBlob = await new Promise(resolve => resizeTo48x48(onImg, resolve));
const resizedOnImg = new Image();
resizedOnImg.src = URL.createObjectURL(resizedOnBlob);
resizedOnImg.onload = async () => {
// Crop the On icon
const croppedOnBlob = await new Promise(resolve => cropIcon(resizedOnImg, resolve));
zip.file(`${iconName}48_On.png`, croppedOnBlob);
// Resize cropped On icon to 16x16
const croppedOnImg = new Image();
croppedOnImg.src = URL.createObjectURL(croppedOnBlob);
croppedOnImg.onload = async () => {
const resized16Blob = await new Promise(resolve => resizeTo16x16(croppedOnImg, resolve));
zip.file(`${iconName}.png`, resized16Blob);
// Process Off icon
let offImg = new Image();
offImg.src = URL.createObjectURL(offIconInput.files[0]);
offImg.onload = async () => {
// Resize Off image to 48x48
const resizedOffBlob = await new Promise(resolve => resizeTo48x48(offImg, resolve));
const resizedOffImg = new Image();
resizedOffImg.src = URL.createObjectURL(resizedOffBlob);
resizedOffImg.onload = async () => {
// Crop the Off icon
const croppedOffBlob = await new Promise(resolve => cropIcon(resizedOffImg, resolve));
zip.file(`${iconName}48_Off.png`, croppedOffBlob);
// Add icons.txt with name;Name;Name format
zip.file('icons.txt', `${iconName};${iconNameCapitalized};${iconNameCapitalized}`);
// Generate and download ZIP
const content = await zip.generateAsync({ type: 'blob' });
const link = document.createElement('a');
link.href = URL.createObjectURL(content);
link.download = `${iconName}_icons.zip`;
link.click();
status.textContent = 'ZIP file downloaded!';
};
};
offImg.onerror = () => status.textContent = 'Error loading Off image.';
};
};
};
onImg.onerror = () => status.textContent = 'Error loading On image.';
});
</script>
</body>
</html>