Skip to content

Migrate admin interface to Admin 5 JSON-Config#779

Open
Copilot wants to merge 3 commits intomainfrom
copilot/rebuild-admin-interface-react
Open

Migrate admin interface to Admin 5 JSON-Config#779
Copilot wants to merge 3 commits intomainfrom
copilot/rebuild-admin-interface-react

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Feb 15, 2026

Replaces Materialize HTML admin interface with Admin 5 React-based JSON-Config format, following the pattern established by ioBroker.esphome.

Changes

Admin Interface (admin/jsonConfig.json5)

  • Two-tab structure: Main Settings (polling interval, retry config) and Devices (management interface)
  • Device table with dynamic refresh, manual add by IP, and delete with confirmation
  • Conditional rendering based on adapter running state

Backend (main.js)

  • Added loadDevices sendTo handler returning devices in JSON-Config table format:
    { 
      native: { 
        devicesTable: [
          { mac, name, ip, connected: "Connected"|"Offline" }
        ] 
      } 
    }
  • Updated addDevice/deleteDevice handlers to parse both legacy string format (obj.message) and JSON-Config object format (obj.message.ip)

Configuration

  • io-package.json: Changed adminUI.config from "materialize" to "json"
  • Added translation keys (EN/DE) for all UI elements
  • Legacy interface preserved as admin/index_m.html.backup

Documentation

  • Updated README with Admin 5 configuration workflow
  • Added CHANGELOG entries for migration
Original prompt

This section details on the original issue you should resolve

<issue_title>Rebuild admin interface to support React Layout (Admin 5)</issue_title>
<issue_description></issue_description>

<agent_instructions>take example by https://github.com/DrozmotiX/ioBroker.esphome</agent_instructions>

Comments on the Issue (you are @copilot in this section)


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI and others added 2 commits February 15, 2026 22:08
Co-authored-by: DutchmanNL <7318445+DutchmanNL@users.noreply.github.com>
Co-authored-by: DutchmanNL <7318445+DutchmanNL@users.noreply.github.com>
Copilot AI changed the title [WIP] Rebuild admin interface to support React Layout Migrate admin interface to Admin 5 JSON-Config Feb 15, 2026
Copilot AI requested a review from DutchmanNL February 15, 2026 22:13
@DutchmanNL DutchmanNL marked this pull request as ready for review February 15, 2026 22:19
Copilot AI review requested due to automatic review settings February 15, 2026 22:19
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Migrates the adapter’s admin UI from the legacy Materialize page to an Admin 5 (React) JSON-Config interface, and updates backend sendTo handlers to support the new device management workflow.

Changes:

  • Added a new Admin 5 JSON-Config UI with tabs for main settings and device management.
  • Updated addDevice / deleteDevice message parsing for JSON-Config compatibility and added a new loadDevices handler for populating the devices table.
  • Switched io-package.json to adminUI.config = "json" and added EN/DE i18n strings; updated README + changelog entries.

Reviewed changes

Copilot reviewed 6 out of 7 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
main.js Adds loadDevices sendTo handler and updates message parsing for addDevice/deleteDevice.
io-package.json Switches admin UI from Materialize to JSON-Config (Admin 5).
admin/jsonConfig.json5 Introduces the new Admin 5 configuration UI (tabs, device table, sendTo buttons).
admin/index_m.html.backup Adds a backup of the legacy Materialize admin page.
admin/i18n/en/translations.json Adds new translation keys for the JSON-Config UI.
admin/i18n/de/translations.json Adds German translations for the JSON-Config UI.
README.md Updates configuration instructions and adds changelog entries for the migration.

Comment on lines 525 to 529
case 'deleteDevice':
// Delete device by IP address or device ID
// eslint-disable-next-line no-case-declarations
const deviceIP = obj.message;
const deviceIP = typeof obj.message === 'string' ? obj.message : obj.message.ip;
this.log.debug(`Delete device request received for IP: ${deviceIP}`);
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

deleteDevice now supports JSON-Config message formats, but the delete logic further down uses this.devices[ip].name as the object ID to delete. handleBasicStates() creates device objects keyed by MAC (deviceData.info.mac) and stores it in this.devices[deviceIP].mac, while .name is the WLED device name. As a result, deletion by IP can fail to remove the correct device tree. Use this.devices[deviceIP].mac (the actual object-id) when calling delDevice().

Copilot uses AI. Check for mistakes.
Comment on lines +571 to +578
const stateObj = await this.getStateAsync(`${device.name}._info._online`);
connectionState = stateObj && stateObj.val ? 'Connected' : 'Offline';
} catch (stateError) {
this.log.debug(`Could not get state for ${device.name}: ${stateError.message}`);
}

devicesList.push({
mac: device.name,
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

loadDevices checks the online state using ${device.name}._info._online, but online states are keyed by MAC (${this.devices[ip].mac}._info._online), and device.name is the WLED device name (see handleBasicStates). This will cause the status column to be wrong/missing. Use device.mac for the state ID.

Suggested change
const stateObj = await this.getStateAsync(`${device.name}._info._online`);
connectionState = stateObj && stateObj.val ? 'Connected' : 'Offline';
} catch (stateError) {
this.log.debug(`Could not get state for ${device.name}: ${stateError.message}`);
}
devicesList.push({
mac: device.name,
const idBase = device.mac || device.name;
const stateObj = await this.getStateAsync(`${idBase}._info._online`);
connectionState = stateObj && stateObj.val ? 'Connected' : 'Offline';
} catch (stateError) {
this.log.debug(`Could not get state for ${device.name}: ${stateError.message}`);
}
devicesList.push({
mac: device.mac || device.name,

Copilot uses AI. Check for mistakes.
Comment on lines +577 to +582
devicesList.push({
mac: device.name,
name: device.clientName || device.name,
ip: ip,
connected: connectionState,
});
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the devicesList.push(...) mapping, mac: device.name is incorrect because .name is the WLED device name, not the MAC address. The MAC is stored as device.mac, and the UI column is labeled "MAC Address". Consider using { mac: device.mac, name: device.name, ... } (and drop device.clientName unless it is actually set elsewhere).

Copilot uses AI. Check for mistakes.
// Main settings tab
"_options": {
"type": "panel",
"label": "Main settings",
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With "i18n": true, tab labels should be translation keys to avoid always showing English. "label": "Main settings" is not present in the i18n files, so it won’t be translated in other languages. Consider switching this to a key (e.g. lblMainSettingsTab) and adding it to EN/DE translations.

Suggested change
"label": "Main settings",
"label": "lblMainSettingsTab",

Copilot uses AI. Check for mistakes.
// Devices tab
"devices": {
"type": "panel",
"label": "Devices",
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The second tab uses "label": "Devices" while i18n is enabled, but there is no translation entry for this literal label. Use an i18n key for the tab label (and add EN/DE translations) so the tab name is localized consistently with the rest of the UI.

Suggested change
"label": "Devices",
"label": "lblDevicesHeader",

Copilot uses AI. Check for mistakes.
Comment on lines +558 to +563
case 'loadDevices':
// Load and return all devices with their current state
this.log.debug('Load devices request received');
try {
const devicesList = [];

Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

loadDevices introduces a new sendTo command used by the Admin 5 UI, but there is no integration test covering its response format (e.g., returning { native: { devicesTable: [] } } when no devices are configured, and a populated table when devices exist). Since this repo already has integration tests for other sendTo commands, please add a similar test to prevent regressions in the Admin 5 device table refresh.

Copilot generated this review using guidance from repository custom instructions.
Comment on lines +515 to +517
const deviceIPToAdd = typeof obj.message === 'string' ? obj.message : obj.message.ip;
// eslint-disable-next-line no-case-declarations
const result = await this.getDeviceJSON(deviceIPToAdd);
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

deviceIPToAdd extraction can throw if obj.message is null/undefined (because typeof null === 'object' and then obj.message.ip crashes). Please guard this by checking obj.message && typeof obj.message === 'object' before reading .ip, and return a clear failure response when no IP was provided.

Copilot uses AI. Check for mistakes.
Comment on lines 527 to 529
// eslint-disable-next-line no-case-declarations
const deviceIP = obj.message;
const deviceIP = typeof obj.message === 'string' ? obj.message : obj.message.ip;
this.log.debug(`Delete device request received for IP: ${deviceIP}`);
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to addDevice, const deviceIP = typeof obj.message === 'string' ? obj.message : obj.message.ip; can throw when obj.message is null/undefined. Add a guard and fail early with a helpful response if no IP was provided.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Rebuild admin interface to support React Layout (Admin 5)

3 participants