296 lines
28 KiB
HTML
296 lines
28 KiB
HTML
<!doctype html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
|
||
<meta name="theme-color" content="#151515" id="themeColorMeta">
|
||
<meta name="description" content="Local GREE air conditioner controller" data-i18n-content="meta.description">
|
||
<link rel="manifest" href="/manifest.webmanifest">
|
||
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
|
||
<link rel="stylesheet" href="/styles.css">
|
||
<script>
|
||
(() => {
|
||
const getCookie = name => document.cookie.split('; ').find(row => row.startsWith(`${name}=`))?.split('=')[1];
|
||
const requested = decodeURIComponent(getCookie('gree_controller_theme') || 'system');
|
||
const resolved = requested === 'light' || requested === 'dark'
|
||
? requested
|
||
: (window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark');
|
||
document.documentElement.dataset.theme = resolved;
|
||
})();
|
||
</script>
|
||
<title>GREE Controller</title>
|
||
</head>
|
||
<body>
|
||
<header class="topbar">
|
||
<div class="brand">
|
||
<div><strong>GREE Controller</strong><small id="connectionLabel" data-i18n="status.connecting">Connecting…</small></div>
|
||
</div>
|
||
<div class="top-actions">
|
||
<select class="toolbar-select" id="languageSelect" data-i18n-aria="controls.language" aria-label="Language">
|
||
<option value="en">English</option>
|
||
</select>
|
||
<select class="toolbar-select theme-select" id="themeSelect" data-i18n-aria="controls.theme" aria-label="Theme">
|
||
<option value="system" data-i18n="theme.system">System</option>
|
||
<option value="light" data-i18n="theme.light">Light</option>
|
||
<option value="dark" data-i18n="theme.dark">Dark</option>
|
||
</select>
|
||
<button class="icon-button" id="refreshButton" data-i18n-title="actions.refresh" data-i18n-aria="actions.refresh" title="Refresh" aria-label="Refresh">↻</button>
|
||
<button class="primary compact" id="discoverButton" data-i18n="actions.discover">Discover</button>
|
||
</div>
|
||
</header>
|
||
|
||
<main>
|
||
<section class="view active" data-view="dashboard">
|
||
<div class="hero">
|
||
<div><span class="eyebrow" data-i18n="dashboard.home">Home</span><h1 data-i18n="dashboard.title">Comfort under control</h1><p id="summaryText" data-i18n="dashboard.loading">Loading devices…</p></div>
|
||
<div class="hero-temp" id="heroTemperature">--<small>°C</small></div>
|
||
</div>
|
||
<div class="metrics" id="metrics"></div>
|
||
<div class="panel house-climate" id="houseClimate"></div>
|
||
<div class="section-heading"><div><span class="eyebrow" data-i18n="nav.zones">Zones</span><h2 data-i18n="dashboard.quickThermostats">Quick thermostats</h2></div></div>
|
||
<div class="list-grid dashboard-zones" id="dashboardZones"></div>
|
||
<div class="section-heading"><div><span class="eyebrow" data-i18n="nav.devices">Devices</span><h2 data-i18n="dashboard.quickControl">Direct device control</h2></div></div>
|
||
<div class="device-grid" id="dashboardDevices"></div>
|
||
</section>
|
||
|
||
<section class="view" data-view="devices">
|
||
<div class="section-heading">
|
||
<div><span class="eyebrow" data-i18n="devices.lan">LAN network</span><h1 data-i18n="nav.devices">Devices</h1></div>
|
||
<button class="secondary" data-open="deviceDialog" data-i18n="devices.addManual">Add manually</button>
|
||
</div>
|
||
<div class="device-grid" id="deviceList"></div>
|
||
</section>
|
||
|
||
<section class="view" data-view="zones">
|
||
<div class="section-heading"><div><span class="eyebrow" data-i18n="zones.automation">Automation</span><h1 data-i18n="nav.zones">Zones</h1></div><button class="secondary" data-open="zoneDialog" data-i18n="zones.new">New zone</button></div>
|
||
<p class="lead" data-i18n="zones.description">Each zone is a fast thermostat. It normally follows the house heating/cooling mode and changes the AC setpoint instead of cycling power.</p>
|
||
<div class="list-grid" id="zoneList"></div>
|
||
</section>
|
||
|
||
<section class="view" data-view="schedules">
|
||
<div class="section-heading"><div><span class="eyebrow" data-i18n="schedules.calendar">Calendar</span><h1 data-i18n="nav.schedules">Schedules</h1></div><button class="secondary" data-open="scheduleDialog" data-i18n="actions.add">Add</button></div>
|
||
<div class="panel schedule-template-panel">
|
||
<div><h3 data-i18n="schedules.quickSetup">Quick schedule setup</h3><p class="field-note" data-i18n="schedules.quickSetupHint">Choose a zone and a ready-made daily rhythm. You can edit every generated entry afterwards.</p></div>
|
||
<select id="schedulePresetZone"></select>
|
||
<div class="template-grid">
|
||
<button class="secondary" data-schedule-template="family" data-i18n="schedules.templateFamily">Family</button>
|
||
<button class="secondary" data-schedule-template="child" data-i18n="schedules.templateChild">Child room</button>
|
||
<button class="secondary" data-schedule-template="bedroom" data-i18n="schedules.templateBedroom">Bedroom</button>
|
||
<button class="secondary" data-schedule-template="workday" data-i18n="schedules.templateWorkday">Workday</button>
|
||
<button class="secondary" data-schedule-template="always" data-i18n="schedules.templateAlways">Always comfort</button>
|
||
</div>
|
||
</div>
|
||
<div class="list-grid" id="scheduleList"></div>
|
||
</section>
|
||
|
||
<section class="view" data-view="automations">
|
||
<div class="section-heading"><div><span class="eyebrow" data-i18n="automations.rules">Rules</span><h1 data-i18n="nav.automations">Automations</h1></div><button class="secondary" data-open="automationDialog" data-i18n="actions.add">Add</button></div>
|
||
<div class="panel automation-hint"><strong data-i18n="automations.smartHintTitle">Use thermostat presets for daily comfort</strong><p data-i18n="automations.smartHint">Schedules and zone presets handle normal heating/cooling automatically. Keep automations for special exceptions so they do not fight the thermostat.</p></div>
|
||
<div class="list-grid" id="automationList"></div>
|
||
</section>
|
||
|
||
<section class="view" data-view="history">
|
||
<div class="section-heading"><div><span class="eyebrow" data-i18n="history.measurements">Measurements</span><h1 data-i18n="history.title">Climate history</h1></div></div>
|
||
<div class="history-tabs" id="historyTabs" role="tablist">
|
||
<button data-history-tab="overview" data-i18n="history.tabOverview">Overview</button>
|
||
<button data-history-tab="zones" data-i18n="history.tabZones">Zones</button>
|
||
<button data-history-tab="devices" data-i18n="history.tabDevices">GREE devices</button>
|
||
<button data-history-tab="sensors" data-i18n="history.tabSensors">HA sensors</button>
|
||
<button data-history-tab="custom" data-i18n="history.tabCustom">Custom chart</button>
|
||
</div>
|
||
<div class="panel chart-panel history-toolbar-panel">
|
||
<div class="chart-toolbar">
|
||
<div id="historyContextControls" class="history-context-controls"></div>
|
||
<select id="historyHours" aria-label="History range">
|
||
<option value="6" data-i18n="history.6h">6 hours</option>
|
||
<option value="24" selected data-i18n="history.24h">24 hours</option>
|
||
<option value="168" data-i18n="history.7d">7 days</option>
|
||
<option value="720" data-i18n="history.30d">30 days</option>
|
||
</select>
|
||
<button class="secondary" id="historyRefresh" data-i18n="actions.refresh">Refresh</button>
|
||
</div>
|
||
<div class="history-summary" id="historySummary"></div>
|
||
</div>
|
||
<div id="historyCustomBuilder" class="history-custom-builder"></div>
|
||
<div id="historyCharts" class="history-charts"></div>
|
||
</section>
|
||
|
||
<section class="view" data-view="settings">
|
||
<div class="section-heading"><div><span class="eyebrow" data-i18n="settings.system">System</span><h1 data-i18n="nav.settings">Settings</h1></div></div>
|
||
<form class="panel form-grid" id="settingsForm">
|
||
<h3 data-i18n="settings.controller">Controller</h3>
|
||
<label><span data-i18n="settings.clientId">Client identifier</span><input name="controller_id" required></label>
|
||
<label><span data-i18n="settings.pollInterval">Poll interval (s)</span><input type="number" name="poll_interval_seconds" min="2" max="3600" required></label>
|
||
<label><span data-i18n="settings.zoneInterval">Zone interval (s)</span><input type="number" name="zone_interval_seconds" min="2" max="3600" required></label>
|
||
<label><span data-i18n="settings.broadcast">Broadcast address</span><input name="discovery_broadcast" placeholder="255.255.255.255:7000" required></label>
|
||
<label><span data-i18n="settings.discoveryTimeout">Discovery timeout (ms)</span><input type="number" name="discovery_timeout_ms" min="300" max="30000" required></label>
|
||
<label class="check"><input type="checkbox" name="simulator_enabled"> <span data-i18n="settings.simulationMode">Simulation mode</span></label>
|
||
<hr>
|
||
<h3 data-i18n="settings.haSensorInput">Home Assistant sensor input</h3>
|
||
<p class="field-note wide" data-i18n="settings.haSensorInputHint">Optional. Used only when a room zone reads an external Home Assistant temperature sensor.</p>
|
||
<label class="wide"><span>URL</span><input type="url" name="ha_url" placeholder="http://homeassistant.local:8123"></label>
|
||
<label class="wide"><span data-i18n="settings.haLongLivedToken">Long-Lived Access Token</span><input type="password" name="ha_token" autocomplete="new-password" data-i18n-placeholder="settings.haTokenKeep" placeholder="Leave empty to keep the saved token"></label>
|
||
<label class="wide"><span data-i18n="settings.defaultEntity">Default room entity_id</span><input name="ha_entity_id" placeholder="sensor.living_room_temperature"></label>
|
||
<label class="wide"><span data-i18n="settings.outdoorEntity">Outdoor temperature entity_id</span><input name="ha_outdoor_entity_id" placeholder="sensor.outdoor_temperature"></label>
|
||
<label class="check wide"><input type="checkbox" name="outdoor_assist_enabled"> <span data-i18n="settings.outdoorAssist">Use outdoor temperature as smart-control assist</span></label>
|
||
<label class="check wide"><input type="checkbox" name="ha_allow_invalid_tls"> <span data-i18n="settings.allowInvalidTls">Allow invalid/self-signed HTTPS certificate</span></label>
|
||
<p class="field-note wide warning-note" data-i18n="settings.allowInvalidTlsHint">Use only for a trusted local Home Assistant server, for example https://10.87.65.2.</p>
|
||
<div class="form-actions wide"><button type="button" class="secondary" id="haTest" data-i18n="settings.testHa">Test HA</button><button class="primary" type="submit" data-i18n="actions.save">Save</button></div>
|
||
<hr>
|
||
<h3 data-i18n="settings.haIntegrationAccess">Home Assistant integration access</h3>
|
||
<p class="field-note wide" data-i18n="settings.haIntegrationHint">Create a controller token and paste it into the GREE Controller integration in Home Assistant.</p>
|
||
<div class="wide token-manager">
|
||
<div id="accessTokenList" class="token-list"></div>
|
||
<div class="form-actions"><button type="button" class="primary" id="createAccessToken" data-i18n="settings.newToken">Create new token</button></div>
|
||
</div>
|
||
</form>
|
||
<div class="panel system-panel" id="systemInfo"></div>
|
||
</section>
|
||
|
||
<section class="view" data-view="logs">
|
||
<div class="section-heading"><div><span class="eyebrow" data-i18n="logs.diagnostics">Diagnostics</span><h1 data-i18n="nav.logs">Events</h1></div><button class="secondary" id="logsRefresh" data-i18n="actions.refresh">Refresh</button></div>
|
||
<div class="panel log-list" id="logList"></div>
|
||
</section>
|
||
</main>
|
||
|
||
<nav class="bottom-nav" data-i18n-aria="nav.navigation" aria-label="Navigation">
|
||
<button class="active" data-nav="dashboard"><span>⌂</span><b data-i18n="nav.dashboard">Dashboard</b></button>
|
||
<button data-nav="devices"><span>▣</span><b data-i18n="nav.devices">Devices</b></button>
|
||
<button data-nav="zones"><span>◎</span><b data-i18n="nav.zones">Zones</b></button>
|
||
<button data-nav="history"><span>⌁</span><b data-i18n="nav.history">History</b></button>
|
||
<button data-nav="more"><span>•••</span><b data-i18n="nav.more">More</b></button>
|
||
</nav>
|
||
|
||
<dialog id="moreDialog" class="sheet">
|
||
<div class="dialog-head"><h2 data-i18n="nav.more">More</h2><button data-close>×</button></div>
|
||
<div class="menu-list">
|
||
<button data-go="schedules"><span data-i18n="nav.schedules">Schedules</span><span>›</span></button>
|
||
<button data-go="automations"><span data-i18n="nav.automations">Automations</span><span>›</span></button>
|
||
<button data-go="settings"><span data-i18n="nav.settings">Settings</span><span>›</span></button>
|
||
<button data-go="logs"><span data-i18n="nav.logsAndEvents">Events and logs</span><span>›</span></button>
|
||
</div>
|
||
</dialog>
|
||
|
||
<dialog id="deviceDialog">
|
||
<form method="dialog" id="deviceForm" class="dialog-form">
|
||
<div class="dialog-head"><h2 data-i18n="devices.add">Add device</h2><button type="button" data-close>×</button></div>
|
||
<label><span data-i18n="common.name">Name</span><input name="name" required data-i18n-placeholder="placeholder.livingRoom" placeholder="Living room"></label>
|
||
<label><span>MAC / CID</span><input name="mac" required placeholder="AABBCCDDEEFF"></label>
|
||
<label><span data-i18n="devices.ipAddress">IP address</span><input name="ip" required placeholder="192.168.1.50"></label>
|
||
<div class="two"><label><span>Port</span><input type="number" name="port" value="7000" min="1" max="65535"></label><label><span data-i18n="devices.protocol">Protocol</span><select name="protocol_version"><option value="0" data-i18n="devices.protocolAuto">Auto (V1 + V2)</option><option value="1">V1 AES-ECB</option><option value="2">V2 AES-GCM</option></select></label></div>
|
||
<label><span data-i18n="devices.keyOptional">Key (optional)</span><input name="key" data-i18n-placeholder="devices.keyPlaceholder" placeholder="Leave empty for automatic bind"></label>
|
||
<label class="check"><input type="checkbox" name="simulated"> <span data-i18n="devices.simulated">Simulated device</span></label>
|
||
<div class="form-actions"><button type="button" class="secondary" data-close data-i18n="actions.cancel">Cancel</button><button class="primary" type="submit" data-i18n="actions.add">Add</button></div>
|
||
</form>
|
||
</dialog>
|
||
|
||
|
||
<dialog id="discoverDialog">
|
||
<form method="dialog" id="discoverForm" class="dialog-form">
|
||
<div class="dialog-head"><h2 data-i18n="discovery.title">Discover GREE devices</h2><button type="button" data-close>×</button></div>
|
||
<p class="field-note" data-i18n="discovery.help">Auto searches both AES-ECB and AES-GCM devices. Multiple passes improve discovery when several Wi-Fi modules answer the same broadcast.</p>
|
||
<label><span data-i18n="discovery.protocol">Discovery protocol</span><select name="protocol_version"><option value="0" data-i18n="devices.protocolAuto">Auto (V1 + V2)</option><option value="1">V1 AES-ECB</option><option value="2">V2 AES-GCM</option></select></label>
|
||
<div class="two"><label><span data-i18n="discovery.passes">Scan passes</span><input type="number" name="passes" min="1" max="10" value="3"></label><label><span data-i18n="discovery.timeout">Total scan time (ms)</span><input type="number" name="timeout_ms" min="500" max="30000" value="6000"></label></div>
|
||
<div class="form-actions"><button type="button" class="secondary" data-close data-i18n="actions.cancel">Cancel</button><button class="primary" type="submit" data-i18n="actions.discover">Discover</button></div>
|
||
</form>
|
||
</dialog>
|
||
|
||
<dialog id="discoveryNamesDialog">
|
||
<form method="dialog" id="discoveryNamesForm" class="dialog-form">
|
||
<div class="dialog-head"><h2 data-i18n="discovery.nameDevices">Name discovered devices</h2><button type="button" data-close>×</button></div>
|
||
<p class="field-note" data-i18n="discovery.nameDevicesHelp">Give each new unit a friendly room name. The technical model and MAC remain available in diagnostics.</p>
|
||
<div id="discoveryNamesList" class="discovery-name-list"></div>
|
||
<div class="form-actions"><button type="button" class="secondary" data-close data-i18n="actions.later">Later</button><button class="primary" type="submit" data-i18n="actions.save">Save</button></div>
|
||
</form>
|
||
</dialog>
|
||
|
||
<dialog id="renameDeviceDialog">
|
||
<form method="dialog" id="renameDeviceForm" class="dialog-form">
|
||
<input type="hidden" name="id">
|
||
<div class="dialog-head"><h2 data-i18n="devices.rename">Rename device</h2><button type="button" data-close>×</button></div>
|
||
<label><span data-i18n="common.name">Name</span><input name="name" required maxlength="80"></label>
|
||
<label><span data-i18n="devices.protocol">Protocol</span><select name="protocol_version"><option value="0" data-i18n="devices.protocolAuto">Auto (V1 + V2)</option><option value="1">V1 AES-ECB</option><option value="2">V2 AES-GCM</option></select></label>
|
||
<p class="field-note" data-i18n="devices.protocolChangeHint">Changing protocol clears the saved device key and performs a new bind on the next request.</p>
|
||
<div class="form-actions"><button type="button" class="secondary" data-close data-i18n="actions.cancel">Cancel</button><button class="primary" type="submit" data-i18n="actions.save">Save</button></div>
|
||
</form>
|
||
</dialog>
|
||
|
||
<dialog id="zoneDialog">
|
||
<form method="dialog" id="zoneForm" class="dialog-form">
|
||
<input type="hidden" name="id">
|
||
<div class="dialog-head"><h2 data-i18n="common.zone">Zone</h2><button type="button" data-close>×</button></div>
|
||
<label><span data-i18n="common.name">Name</span><input name="name" required data-i18n-placeholder="placeholder.livingRoom" placeholder="Living room"></label>
|
||
<label><span data-i18n="common.device">Device</span><select name="device_id" required></select></label>
|
||
<div class="two"><label><span data-i18n="zones.modePolicy">Mode policy</span><select name="mode_policy"><option value="house" data-i18n="zones.followHouse">Follow house mode</option><option value="cool" data-i18n="mode.cool">Cooling only</option><option value="heat" data-i18n="mode.heat">Heating only</option></select></label><label><span data-i18n="common.targetC">Manual target °C</span><input type="number" step="0.5" min="8" max="30" name="setpoint" value="23"></label></div>
|
||
<details class="profile-editor"><summary data-i18n="zones.profileTemperatures">Comfort / sleep / away temperatures</summary>
|
||
<div class="profile-grid"><strong data-i18n="mode.cool">Cooling</strong><label><span data-i18n="preset.comfort">Comfort</span><input type="number" step="0.5" name="cool_comfort_setpoint" value="23"></label><label><span data-i18n="preset.sleep">Sleep</span><input type="number" step="0.5" name="cool_sleep_setpoint" value="24.5"></label><label><span data-i18n="preset.away">Away</span><input type="number" step="0.5" name="cool_away_setpoint" value="27"></label></div>
|
||
<div class="profile-grid"><strong data-i18n="mode.heat">Heating</strong><label><span data-i18n="preset.comfort">Comfort</span><input type="number" step="0.5" name="heat_comfort_setpoint" value="21"></label><label><span data-i18n="preset.sleep">Sleep</span><input type="number" step="0.5" name="heat_sleep_setpoint" value="19"></label><label><span data-i18n="preset.away">Away</span><input type="number" step="0.5" name="heat_away_setpoint" value="17"></label></div>
|
||
</details>
|
||
<div class="two"><label><span data-i18n="zones.hysteresis">Hysteresis °C</span><input type="number" step="0.1" min="0.1" max="5" name="hysteresis" value="0.6"></label><label><span data-i18n="zones.source">Temperature strategy</span><select name="sensor_source"><option value="combined" selected data-i18n="zones.combinedSensor">GREE + room sensor</option><option value="device" data-i18n="zones.greeSensor">GREE only</option><option value="home_assistant" data-i18n="zones.externalSensor">Room sensor only</option></select></label></div>
|
||
<div id="externalSensorFields">
|
||
<label><span data-i18n="zones.roomSensorEntity">Room sensor entity_id</span><input name="ha_entity_id" placeholder="sensor.living_room_temperature"></label>
|
||
<div class="two"><label><span data-i18n="zones.roomSensorWeight">Room sensor weight %</span><input type="number" step="5" min="0" max="100" name="external_sensor_weight_percent" value="40"></label><label><span data-i18n="zones.maxDifference">Max. sensor difference °C</span><input type="number" step="0.1" min="0.1" max="20" name="max_sensor_difference" value="3.0"></label></div>
|
||
<p class="field-note" data-i18n="zones.sensorHelp">The room sensor is assigned only to this zone. If it becomes unavailable, the controller falls back to the GREE sensor.</p>
|
||
</div>
|
||
<div class="two"><label><span data-i18n="zones.minAdjust">Minimum adjustment interval (s)</span><input type="number" min="15" name="min_adjust_seconds" value="120"></label><label><span data-i18n="zones.standbyOffset">Standby setpoint offset °C</span><input type="number" step="0.5" min="0.5" max="8" name="standby_offset_c" value="2"></label></div>
|
||
<label class="check"><input type="checkbox" name="smart_fan" checked> <span data-i18n="zones.smartFan">Smart fan assist</span></label>
|
||
<input type="hidden" name="min_on_seconds" value="180"><input type="hidden" name="min_off_seconds" value="180">
|
||
<label class="check"><input type="checkbox" name="enabled" checked> <span data-i18n="common.enabled">Enabled</span></label>
|
||
<div class="form-actions"><button type="button" class="secondary" data-close data-i18n="actions.cancel">Cancel</button><button class="primary" type="submit" data-i18n="actions.save">Save</button></div>
|
||
</form>
|
||
</dialog>
|
||
|
||
<dialog id="scheduleDialog">
|
||
<form method="dialog" id="scheduleForm" class="dialog-form">
|
||
<input type="hidden" name="id">
|
||
<div class="dialog-head"><h2 data-i18n="common.schedule">Schedule</h2><button type="button" data-close>×</button></div>
|
||
<label><span data-i18n="common.name">Name</span><input name="name" required data-i18n-placeholder="placeholder.night" placeholder="Night"></label>
|
||
<label><span data-i18n="common.zone">Zone</span><select name="zone_id" required></select></label>
|
||
<fieldset><legend data-i18n="schedules.weekdays">Weekdays</legend><div class="days"><label><input type="checkbox" name="weekday" value="1" checked><span data-day="1">Mon</span></label><label><input type="checkbox" name="weekday" value="2" checked><span data-day="2">Tue</span></label><label><input type="checkbox" name="weekday" value="3" checked><span data-day="3">Wed</span></label><label><input type="checkbox" name="weekday" value="4" checked><span data-day="4">Thu</span></label><label><input type="checkbox" name="weekday" value="5" checked><span data-day="5">Fri</span></label><label><input type="checkbox" name="weekday" value="6"><span data-day="6">Sat</span></label><label><input type="checkbox" name="weekday" value="7"><span data-day="7">Sun</span></label></div></fieldset>
|
||
<div class="two"><label><span data-i18n="common.from">From</span><input type="time" name="start_time" value="22:00" required></label><label><span data-i18n="common.to">To</span><input type="time" name="end_time" value="06:00" required></label></div>
|
||
<label><span data-i18n="schedules.profile">Profile</span><select name="preset"><option value="comfort" data-i18n="preset.comfort">Comfort</option><option value="sleep" data-i18n="preset.sleep">Sleep</option><option value="away" data-i18n="preset.away">Away</option><option value="custom" data-i18n="preset.custom">Custom temperature</option></select></label>
|
||
<label id="scheduleSetpointField"><span data-i18n="common.temperatureC">Custom temperature °C</span><input type="number" name="setpoint" step="0.5" min="8" max="30" value="23"></label>
|
||
<label class="check"><input type="checkbox" name="enabled" checked> <span data-i18n="common.enabled">Enabled</span></label>
|
||
<div class="form-actions"><button type="button" class="secondary" data-close data-i18n="actions.cancel">Cancel</button><button class="primary" type="submit" data-i18n="actions.save">Save</button></div>
|
||
</form>
|
||
</dialog>
|
||
|
||
<dialog id="automationDialog">
|
||
<form method="dialog" id="automationForm" class="dialog-form">
|
||
<input type="hidden" name="id">
|
||
<div class="dialog-head"><h2 data-i18n="common.automation">Automation</h2><button type="button" data-close>×</button></div>
|
||
<label><span data-i18n="common.name">Name</span><input name="name" required data-i18n-placeholder="automations.namePlaceholder" placeholder="Emergency cooling"></label>
|
||
<label><span data-i18n="automations.trigger">Trigger</span><select name="trigger_kind"><option value="temperature_above" data-i18n="automations.tempAbove">Temperature above</option><option value="temperature_below" data-i18n="automations.tempBelow">Temperature below</option><option value="time" data-i18n="automations.time">Time</option></select></label>
|
||
<label><span data-i18n="automations.measurementDevice">Measurement device</span><select name="trigger_device_id"></select></label>
|
||
<div class="two"><label><span data-i18n="automations.thresholdC">Threshold °C</span><input type="number" step="0.1" name="threshold" value="27"></label><label><span data-i18n="automations.time">Time</span><input type="time" name="at_time" value="08:00"></label></div>
|
||
<hr><h3 data-i18n="automations.action">Action</h3>
|
||
<label><span data-i18n="common.device">Device</span><select name="action_device_id" required></select></label>
|
||
<div class="two"><label><span data-i18n="common.power">Power</span><select name="action_power"><option value="" data-i18n="actions.noChange">No change</option><option value="true" data-i18n="actions.turnOn">Turn on</option><option value="false" data-i18n="actions.turnOff">Turn off</option></select></label><label><span data-i18n="common.mode">Mode</span><select name="action_mode"><option value="" data-i18n="actions.noChange">No change</option><option value="cool" data-i18n="mode.cool">Cooling</option><option value="heat" data-i18n="mode.heat">Heating</option><option value="dry" data-i18n="mode.dry">Dry</option><option value="fan" data-i18n="mode.fan">Fan</option><option value="auto" data-i18n="mode.auto">Auto</option></select></label></div>
|
||
<div class="two"><label><span data-i18n="common.targetC">Target °C</span><input type="number" step="0.1" min="8" max="30" name="action_target_temperature"></label><label><span data-i18n="common.cooldownSeconds">Cooldown (s)</span><input type="number" min="30" name="cooldown_seconds" value="300"></label></div>
|
||
<label class="check"><input type="checkbox" name="enabled" checked> <span data-i18n="common.enabled">Enabled</span></label>
|
||
<div class="form-actions"><button type="button" class="secondary" data-close data-i18n="actions.cancel">Cancel</button><button class="primary" type="submit" data-i18n="actions.save">Save</button></div>
|
||
</form>
|
||
</dialog>
|
||
|
||
<dialog id="generatedTokenDialog" class="auth-dialog">
|
||
<div class="dialog-form">
|
||
<div class="dialog-head"><h2 data-i18n="settings.tokenCreated">Token created</h2><button type="button" data-close>×</button></div>
|
||
<p class="field-note" data-i18n="settings.tokenCreatedHint">Copy this token now. It will not be shown again.</p>
|
||
<label><span data-i18n="auth.token">Access token</span><input id="generatedAccessToken" class="mono" readonly></label>
|
||
<div class="form-actions"><button type="button" class="secondary" id="copyAccessToken" data-i18n="actions.copy">Copy</button><button type="button" class="primary" data-close data-i18n="actions.done">Done</button></div>
|
||
</div>
|
||
</dialog>
|
||
|
||
<dialog id="tokenDialog" class="auth-dialog">
|
||
<form method="dialog" id="tokenForm" class="dialog-form">
|
||
<div class="brand large"><div><strong>GREE Controller</strong><small data-i18n="auth.required">Authentication required</small></div></div>
|
||
<label><span data-i18n="auth.token">Access token</span><input type="password" name="token" required autocomplete="current-password"></label>
|
||
<button class="primary" type="submit" data-i18n="auth.connect">Connect</button>
|
||
</form>
|
||
</dialog>
|
||
|
||
<div id="toastStack" class="toast-stack" role="status" aria-live="polite" aria-atomic="false"></div>
|
||
<script src="/app.js" defer></script>
|
||
</body>
|
||
</html>
|