595 lines
65 KiB
HTML
595 lines
65 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="__GREE_BASE_PATH__/manifest.webmanifest">
|
||
<link rel="icon" href="__GREE_BASE_PATH__/favicon.svg" type="image/svg+xml">
|
||
<link rel="stylesheet" href="__GREE_STYLES_ASSET__">
|
||
<script src="__GREE_THEME_INIT_ASSET__"></script>
|
||
<title>GREE Controller</title>
|
||
</head>
|
||
<body>
|
||
<header class="topbar">
|
||
<div class="brand">
|
||
<div class="brand-line"><strong>GREE Controller</strong><small id="connectionLabel" class="connection-dot connecting" role="status" aria-label="Connecting" title="Connecting"></small></div>
|
||
</div>
|
||
<div class="toolbar-runtime-status" id="toolbarRuntimeStatus" aria-live="polite">0/0</div>
|
||
<div class="top-actions">
|
||
<div class="toolbar-picker" data-i18n-title="controls.language" title="Language">
|
||
<span class="toolbar-picker-value" id="languageLabel" aria-hidden="true">EN</span>
|
||
<span class="toolbar-picker-chevron" aria-hidden="true">⌄</span>
|
||
<select class="toolbar-picker-select" id="languageSelect" data-i18n-aria="controls.language" aria-label="Language">
|
||
<option value="en">English</option>
|
||
</select>
|
||
</div>
|
||
<div class="toolbar-picker" data-i18n-title="controls.theme" title="Theme">
|
||
<span class="toolbar-picker-value theme-picker-value" id="themeIcon" aria-hidden="true">◐</span>
|
||
<span class="toolbar-picker-chevron" aria-hidden="true">⌄</span>
|
||
<select class="toolbar-picker-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>
|
||
</div>
|
||
<button class="icon-button toolbar-icon-control" id="refreshButton" data-i18n-title="actions.refresh" data-i18n-aria="actions.refresh" title="Refresh" aria-label="Refresh">↻</button>
|
||
</div>
|
||
<div class="connection-banner" id="connectionBanner" role="status" aria-live="polite" hidden>
|
||
<span class="connection-banner-dot" aria-hidden="true"></span>
|
||
<div><strong id="connectionBannerTitle">Disconnected</strong><span id="connectionBannerText">Live updates are unavailable. Displayed data may be stale.</span></div>
|
||
<button class="secondary" type="button" id="connectionRetry" data-i18n="actions.retry">Retry</button>
|
||
</div>
|
||
<div class="simulation-mode-banner" id="simulationModeBanner" role="status" aria-live="polite" hidden>
|
||
<strong data-i18n="simulation.bannerTitle">SIMULATION MODE</strong>
|
||
<span data-i18n="simulation.bannerText">Simulation is enabled. Verify the mode before relying on controller output.</span>
|
||
</div>
|
||
</header>
|
||
|
||
<main>
|
||
<section class="view active" data-view="dashboard">
|
||
<nav class="dashboard-tabs" data-i18n-aria="dashboard.sections" aria-label="Dashboard sections">
|
||
<button type="button" class="active" data-dashboard-tab="main" data-i18n="dashboard.tabMain">Main</button>
|
||
<button type="button" data-dashboard-tab="thermostats" data-i18n="dashboard.tabThermostats">Thermostats</button>
|
||
<button type="button" data-dashboard-tab="manual" data-i18n="dashboard.tabManual">Manual control</button>
|
||
</nav>
|
||
|
||
<div class="dashboard-tab-panel active" data-dashboard-panel="main">
|
||
<div class="hero dashboard-summary-card">
|
||
<div class="dashboard-summary-copy"><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="dashboard-summary-status"><div class="metrics" id="metrics"></div><div class="hero-temp" id="heroTemperature">--<small>°C</small></div></div>
|
||
</div>
|
||
<div class="panel dashboard-global-panel">
|
||
<div class="dashboard-global-power">
|
||
<div><span class="eyebrow" data-i18n="house.powerAllEyebrow">Global power</span><h3 data-i18n="house.powerAllTitle">All air conditioners</h3><p data-i18n="house.powerAllHint">Master power for thermostat zones. Turn all off powers units down without changing group-control settings; turn all on resumes eligible zones.</p></div>
|
||
<div class="house-power-actions"><button id="housePowerOn" type="button" class="secondary" data-action="house-power" data-value="true" data-i18n="house.powerOnAll">Turn all on</button><button id="housePowerOff" type="button" class="secondary" data-action="house-power" data-value="false" data-i18n="house.powerOffAll">Turn all off</button></div>
|
||
</div>
|
||
<div class="dashboard-global-divider"></div>
|
||
<div class="house-climate dashboard-house-climate" id="houseClimate"></div>
|
||
</div>
|
||
<section class="panel dashboard-section-panel">
|
||
<div class="dashboard-section-head compact-head">
|
||
<div><span class="eyebrow" data-i18n="plan.eyebrow">Control plan</span><h2 data-i18n="plan.title">What happens next</h2></div>
|
||
</div>
|
||
<div class="automation-plan-grid" id="controlPlan"></div>
|
||
</section>
|
||
</div>
|
||
|
||
<div class="dashboard-tab-panel" data-dashboard-panel="thermostats" hidden>
|
||
<section class="dashboard-primary-section">
|
||
<div class="dashboard-section-head">
|
||
<div><span class="eyebrow" data-i18n="groups.eyebrow">Group control</span><h2 data-i18n="groups.dashboardTitle">Control selected air conditioners</h2><p data-i18n="groups.dashboardHint">Group ON activates its thermostats; Group OFF powers its units down and releases group ownership. You can also choose a mode, preset, or custom temperature.</p></div>
|
||
<div class="dashboard-section-actions"><span class="badge" id="dashboardGroupCount">0</span><button type="button" class="secondary" data-go="groups" data-i18n="groups.manage">Manage groups</button></div>
|
||
</div>
|
||
<div class="group-quick-grid dashboard-group-strip" id="dashboardGroups"></div>
|
||
</section>
|
||
<section class="panel dashboard-section-panel">
|
||
<div class="dashboard-section-head compact-head">
|
||
<div><span class="eyebrow" data-i18n="nav.zones">Zones</span><h2 data-i18n="dashboard.quickThermostats">Thermostats</h2></div>
|
||
<span class="badge" id="dashboardZoneCount">0</span>
|
||
</div>
|
||
<div class="list-grid dashboard-zones" id="dashboardZones"></div>
|
||
</section>
|
||
</div>
|
||
|
||
<div class="dashboard-tab-panel" data-dashboard-panel="manual" hidden>
|
||
<section class="panel dashboard-section-panel">
|
||
<div class="dashboard-section-head compact-head">
|
||
<div><span class="eyebrow" data-i18n="nav.devices">Devices</span><h2 data-i18n="dashboard.quickControl">Manual control</h2></div>
|
||
<span class="badge" id="dashboardDeviceCount">0</span>
|
||
</div>
|
||
<div class="device-grid" id="dashboardDevices"></div>
|
||
</section>
|
||
</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>
|
||
<div class="section-heading-actions"><button class="primary compact" id="discoverButton" data-i18n="actions.discover">Discover</button><button class="secondary" data-open="deviceDialog" data-i18n="devices.addManual">Add manually</button></div>
|
||
</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">Zones define thermostat logic and sensor configuration. Use Quick thermostats on the Dashboard for current temperature changes and presets.</p>
|
||
<div class="list-grid" id="zoneList"></div>
|
||
</section>
|
||
|
||
<section class="view" data-view="groups">
|
||
<div class="section-heading"><div><span class="eyebrow" data-i18n="groups.eyebrow">Group control</span><h1 data-i18n="nav.groups">Groups</h1></div><button class="secondary" data-open="groupDialog" data-i18n="groups.new">New group</button></div>
|
||
<p class="lead" data-i18n="groups.description">Combine thermostat zones and control only that set. Turning a group on activates its thermostats; turning it off powers its units down and releases group ownership.</p>
|
||
<div class="list-grid" id="groupList"></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="simulation">
|
||
<div class="section-heading simulation-page-head"><div><span class="eyebrow" data-i18n="simulation.eyebrow">Simulator</span><h1 data-i18n="simulation.title">Automation simulator</h1></div><div class="simulation-head-actions"><button class="secondary" id="simulationOpenTab" data-i18n="simulation.openTab">Open only simulator in new tab</button><button class="secondary" id="simulationFullscreen" data-i18n="simulation.fullscreen">Full screen</button><button class="secondary" id="simulationRefresh" data-i18n="actions.refresh">Refresh</button></div></div>
|
||
<p class="lead" data-i18n="simulation.description">Live preview of the thermostat and automation logic. The flow board shows how sensor readings, schedules, night mode and thermostat decisions become commands sent to each GREE unit.</p>
|
||
<div class="panel simulation-scope-toolbar"><label><span data-i18n="simulation.viewBy">View by</span><select id="simulationScope"><option value="units" data-i18n="simulation.unitsView">Individual units</option><option value="groups" data-i18n="simulation.groupsView">Groups</option></select></label><label><span data-i18n="simulation.show">Show</span><select id="simulationTarget"></select></label></div>
|
||
<div class="simulation-summary-grid" id="simulationSummary"></div>
|
||
<div class="panel simulation-flow-shell">
|
||
<div class="simulation-flow-head"><div><span class="eyebrow" data-i18n="simulation.flowEyebrow">Live flow</span><h2 data-i18n="simulation.flowTitle">Automation flow board</h2></div><div class="flow-legend"><span><i class="flow-legend-dot input"></i><b data-i18n="simulation.legendInput">Input</b></span><span><i class="flow-legend-dot logic"></i><b data-i18n="simulation.legendLogic">Logic</b></span><span><i class="flow-legend-dot action"></i><b data-i18n="simulation.legendAction">Action</b></span></div></div>
|
||
<div class="simulation-flow-scroll"><div class="simulation-flow-board" id="simulationFlowBoard"></div></div>
|
||
</div>
|
||
<div class="panel simulation-timeline-panel">
|
||
<div class="simulation-section-head"><div><span class="eyebrow" data-i18n="simulation.timelineEyebrow">Timeline</span><h2 data-i18n="simulation.timelineTitle">What will happen next</h2></div></div>
|
||
<div class="simulation-timeline" id="simulationTimeline"></div>
|
||
</div>
|
||
<div class="simulation-section-head"><div><span class="eyebrow" data-i18n="simulation.rulesEyebrow">Rules</span><h2 data-i18n="simulation.rulesTitle">Automation rules</h2></div></div>
|
||
<div class="simulation-rules-grid" id="simulationRules"></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>
|
||
<label class="history-range-control"><span data-i18n="history.range">Range</span><select id="historyHours" data-i18n-aria="history.range" 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>
|
||
<option value="2160" data-i18n="history.90d">90 days</option>
|
||
<option value="8760" data-i18n="history.1y">1 year</option>
|
||
</select></label>
|
||
<button class="secondary history-refresh-button" 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="night">
|
||
<div class="section-heading"><div><span class="eyebrow" data-i18n="night.eyebrow">Comfort</span><h1 data-i18n="nav.nightMode">Night mode</h1></div></div>
|
||
<p class="lead" data-i18n="night.description">Configure quieter thermostat behavior for sleeping hours without mixing it with application settings.</p>
|
||
<form class="settings-form standalone-settings-form" id="nightModeForm">
|
||
<section class="panel settings-block">
|
||
<div class="settings-block-head"><div><h3 data-i18n="settings.nightMode">Night mode</h3><p data-i18n="settings.nightModeHint">During selected hours the thermostat limits fan speed and can request Quiet on supported units.</p></div></div>
|
||
<div class="settings-grid">
|
||
<label class="check wide"><input type="checkbox" name="night_mode_enabled"> <span data-i18n="settings.nightModeEnabled">Enable night mode</span></label>
|
||
<label><span data-i18n="settings.nightStart">Start</span><input type="time" name="night_mode_start" value="22:00"></label>
|
||
<label><span data-i18n="settings.nightEnd">End</span><input type="time" name="night_mode_end" value="06:00"></label>
|
||
<label><span data-i18n="settings.nightMaxFan">Maximum fan speed</span><select name="night_mode_max_fan_speed"><option value="1" data-i18n="fan.low">Low</option><option value="2" data-i18n="fan.mediumLow">Medium-low</option><option value="3" data-i18n="fan.medium">Medium</option><option value="4" data-i18n="fan.mediumHigh">Medium-high</option><option value="5" data-i18n="fan.high">High</option></select></label>
|
||
<label class="check"><input type="checkbox" name="night_mode_force_quiet"> <span data-i18n="settings.nightForceQuiet">Use Quiet when supported</span></label>
|
||
<label class="check wide"><input type="checkbox" name="night_mode_native_sleep"> <span data-i18n="settings.nightNativeSleep">Use native Sleep when supported</span></label>
|
||
</div>
|
||
</section>
|
||
<div class="settings-save-bar"><span data-i18n="night.saveHint">Save night-mode changes.</span><button class="primary" type="submit" data-i18n="actions.save">Save</button></div>
|
||
</form>
|
||
</section>
|
||
|
||
<section class="view" data-view="homeassistant">
|
||
<div class="section-heading"><div><span class="eyebrow" data-i18n="homeAssistant.eyebrow">Integration</span><h1 data-i18n="nav.homeAssistant">Home Assistant / Sensors</h1></div></div>
|
||
<p class="lead" data-i18n="homeAssistant.description">Connection, temperature sensors, friendly aliases and access tokens for the Home Assistant integration.</p>
|
||
<form class="settings-form standalone-settings-form" id="homeAssistantForm">
|
||
<section class="panel settings-block">
|
||
<div class="settings-block-head"><div><h3 data-i18n="settings.haSensorInput">Home Assistant sensor input</h3><p data-i18n="settings.haSensorInputHint">Optional. Used when a room zone reads an external Home Assistant temperature sensor.</p></div></div>
|
||
<div class="settings-grid">
|
||
<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="wide"><span data-i18n="settings.haSensorStaleAfterMinutes">Maximum HA sensor reading age (min)</span><input type="number" name="ha_sensor_stale_after_minutes" min="1" max="1440" step="1" value="5"></label>
|
||
<p class="field-note wide" data-i18n="settings.haSensorStaleAfterHint">If the HA sensor is not updated within this time, the reading is treated as stale. The thermostat falls back to the GREE sensor when available.</p>
|
||
<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://192.168.50.25.</p>
|
||
<div class="wide sensor-alias-manager">
|
||
<div class="settings-subhead"><div><strong data-i18n="settings.sensorAliases">Sensor aliases</strong><small data-i18n="settings.sensorAliasesHint">Friendly names are used in charts and selectors; the original entity_id remains unchanged.</small></div></div>
|
||
<div id="sensorAliasList" class="sensor-alias-list"></div>
|
||
<div class="sensor-alias-add"><input id="sensorAliasEntity" placeholder="sensor.gabinet_temperature"><input id="sensorAliasName" data-i18n-placeholder="settings.aliasPlaceholder" placeholder="Gabinet"><button type="button" class="secondary" id="addSensorAlias" data-i18n="actions.add">Add</button></div>
|
||
</div>
|
||
<div class="form-actions wide"><button type="button" class="secondary" id="haTest" data-i18n="settings.testHa">Test HA</button></div>
|
||
</div>
|
||
</section>
|
||
<section class="panel settings-block">
|
||
<div class="settings-block-head"><div><h3 data-i18n="settings.haIntegrationAccess">Home Assistant integration access</h3><p data-i18n="settings.haIntegrationHint">Create a controller token and paste it into the GREE Controller integration in Home Assistant.</p></div></div>
|
||
<div class="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>
|
||
</section>
|
||
<div class="settings-save-bar"><span data-i18n="homeAssistant.saveHint">Save Home Assistant and sensor changes.</span><button class="primary" type="submit" data-i18n="actions.save">Save</button></div>
|
||
</form>
|
||
</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>
|
||
<div class="settings-mode-tabs" role="tablist" data-i18n-aria="settings.settingsSections" aria-label="Settings sections">
|
||
<button type="button" class="active" role="tab" aria-selected="true" data-settings-tab="app" data-i18n="settings.applicationSettings">Application</button>
|
||
<button type="button" role="tab" aria-selected="false" data-settings-tab="gree" data-i18n="settings.greeSettings">GREE</button>
|
||
</div>
|
||
<form class="settings-form" id="settingsForm" novalidate>
|
||
<div class="settings-pane" data-settings-pane="app">
|
||
<section class="panel settings-block">
|
||
<div class="settings-block-head"><div><h3 data-i18n="settings.applicationRuntime">Application runtime</h3><p data-i18n="settings.applicationRuntimeHint">Application-wide behavior that does not change GREE transport or discovery.</p></div></div>
|
||
<div class="settings-grid">
|
||
<label class="check wide"><input type="checkbox" name="simulator_enabled"> <span data-i18n="settings.simulationMode">Simulation mode</span></label>
|
||
<p class="field-note wide warning-note" data-i18n="settings.simulationModeHint">When enabled, a prominent warning is shown throughout the application.</p>
|
||
</div>
|
||
</section>
|
||
<section class="panel settings-block">
|
||
<div class="settings-block-head"><div><h3 data-i18n="notifications.title">Notifications</h3><p data-i18n="notifications.hint">Send important events or only problems/anomalies.</p></div></div>
|
||
<div class="settings-grid">
|
||
<label class="check wide"><input type="checkbox" name="notifications_enabled"> <span data-i18n="notifications.enable">Enable notifications</span></label>
|
||
<label><span data-i18n="notifications.mode">Mode</span><select name="notifications_mode"><option value="problems" data-i18n="notifications.problems">Errors and anomalies only</option><option value="important" data-i18n="notifications.important">Important events + problems</option></select></label>
|
||
<label><span data-i18n="notifications.provider">Provider</span><select name="notifications_provider"><option value="pushover">Pushover</option><option value="slack">Slack</option><option value="discord">Discord</option></select></label>
|
||
<div class="wide notification-provider" data-notification-provider="pushover"><label><span data-i18n="notifications.pushoverToken">Pushover application token</span><input type="password" name="pushover_app_token" autocomplete="new-password" data-i18n-placeholder="settings.secretKeep" placeholder="Leave empty to keep saved secret"></label><label><span data-i18n="notifications.pushoverUser">Pushover user/group key</span><input type="password" name="pushover_user_key" autocomplete="new-password" data-i18n-placeholder="settings.secretKeep" placeholder="Leave empty to keep saved secret"></label></div>
|
||
<label class="wide notification-provider" data-notification-provider="slack"><span data-i18n="notifications.slackWebhook">Slack incoming webhook</span><input type="password" name="slack_webhook_url" autocomplete="new-password" placeholder="https://hooks.slack.com/services/…"></label>
|
||
<label class="wide notification-provider" data-notification-provider="discord"><span data-i18n="notifications.discordWebhook">Discord webhook</span><input type="password" name="discord_webhook_url" autocomplete="new-password" placeholder="https://discord.com/api/webhooks/…"></label>
|
||
<label><span data-i18n="notifications.cooldown">Cooldown (s)</span><input type="number" name="notification_cooldown_seconds" min="30" max="86400" value="300"></label>
|
||
<label><span data-i18n="notifications.failureThreshold">Communication failures before alert</span><input type="number" name="notification_failure_threshold" min="2" max="100" value="3"></label>
|
||
<label><span data-i18n="notifications.targetTimeout">Target not reached timeout (min)</span><input type="number" name="notification_target_timeout" min="5" max="1440" value="60"></label>
|
||
<fieldset class="notification-alert-types wide">
|
||
<legend data-i18n="notifications.alertTypes">Alert types</legend>
|
||
<p class="field-note" data-i18n="notifications.alertTypesHint">Choose exactly which alerts may be delivered. Thresholds above still define when applicable alerts are raised.</p>
|
||
<div class="notification-alert-grid">
|
||
<label class="check"><input type="checkbox" name="notification_alert_stale_sensor"> <span data-i18n="notifications.alertStaleSensor">Stale Home Assistant sensor readings</span></label>
|
||
<label class="check"><input type="checkbox" name="notification_alert_sensor_errors"> <span data-i18n="notifications.alertSensorErrors">Other Home Assistant sensor errors</span></label>
|
||
<label class="check"><input type="checkbox" name="notification_alert_communication"> <span data-i18n="notifications.alertCommunication">Device communication problems</span></label>
|
||
<label class="check"><input type="checkbox" name="notification_alert_target_timeout"> <span data-i18n="notifications.alertTargetTimeout">Target temperature timeout</span></label>
|
||
<label class="check"><input type="checkbox" name="notification_alert_automation"> <span data-i18n="notifications.alertAutomation">Automation errors and conflicts</span></label>
|
||
<label class="check"><input type="checkbox" name="notification_alert_control_errors"> <span data-i18n="notifications.alertControlErrors">Thermostat and group control errors</span></label>
|
||
<label class="check"><input type="checkbox" name="notification_alert_important_events"> <span data-i18n="notifications.alertImportantEvents">Important state changes</span></label>
|
||
<label class="check"><input type="checkbox" name="notification_alert_other"> <span data-i18n="notifications.alertOther">Other warnings and errors</span></label>
|
||
</div>
|
||
</fieldset>
|
||
<div class="form-actions wide"><button type="button" class="secondary" id="testNotifications" data-i18n="notifications.test">Send test notification</button></div>
|
||
</div>
|
||
</section>
|
||
<section class="panel settings-block">
|
||
<div class="settings-block-head"><div><h3 data-i18n="settings.metricsAndLogs">Metrics and logs</h3><p data-i18n="settings.compactionHint">SQLite keeps recent data locally and compacts older samples to the resolution used by charts.</p></div></div>
|
||
<div class="settings-grid">
|
||
<label><span data-i18n="settings.retentionDays">Local metric retention (days)</span><input type="number" name="history_retention_days" min="1" max="3650" required></label>
|
||
<label><span data-i18n="settings.eventRetentionDays">Event/log retention (days)</span><input type="number" name="event_log_retention_days" min="1" max="3650" required></label>
|
||
<label class="check wide"><input type="checkbox" name="history_compaction_enabled"> <span data-i18n="settings.compaction">Compact old metrics</span></label>
|
||
</div>
|
||
</section>
|
||
<section class="panel settings-block">
|
||
<div class="settings-block-head"><div><h3 data-i18n="settings.influx">Long-term InfluxDB history</h3><p data-i18n="settings.influxHint">Optional archive for older history. InfluxDB 1.x and 2.x are supported.</p></div></div>
|
||
<div class="settings-grid">
|
||
<label class="check wide"><input type="checkbox" name="influx_enabled"> <span data-i18n="settings.influxEnabled">Enable InfluxDB archive</span></label>
|
||
<label><span data-i18n="settings.influxVersion">InfluxDB version</span><select name="influx_version"><option value="1">1.x</option><option value="2">2.x</option></select></label>
|
||
<label><span data-i18n="settings.influxThreshold">Use archive for history older than (days)</span><input type="number" name="influx_threshold_days" min="1" max="3650" value="30"></label>
|
||
<label class="wide"><span>URL</span><input type="url" name="influx_url" placeholder="http://influxdb:8086"></label>
|
||
<div class="wide influx-fields" data-influx-fields="1">
|
||
<label><span data-i18n="settings.influxDatabase">Database</span><input name="influx_database" placeholder="gree_controller"></label>
|
||
<label><span data-i18n="settings.influxUsername">Username</span><input name="influx_username"></label>
|
||
<label><span data-i18n="settings.influxPassword">Password</span><input type="password" name="influx_password" autocomplete="new-password" data-i18n-placeholder="settings.secretKeep" placeholder="Leave empty to keep saved secret"></label>
|
||
</div>
|
||
<div class="wide influx-fields" data-influx-fields="2">
|
||
<label><span data-i18n="settings.influxOrg">Organization</span><input name="influx_org"></label>
|
||
<label><span data-i18n="settings.influxBucket">Bucket</span><input name="influx_bucket" placeholder="gree_controller"></label>
|
||
<label><span data-i18n="settings.influxToken">Token</span><input type="password" name="influx_token" autocomplete="new-password" data-i18n-placeholder="settings.secretKeep" placeholder="Leave empty to keep saved secret"></label>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
<section class="panel settings-block">
|
||
<div class="settings-block-head"><div><h3 data-i18n="settings.debug">On-screen debug</h3><p data-i18n="settings.debugHint">Live diagnostics displayed on every application page.</p></div></div>
|
||
<div class="settings-grid">
|
||
<label class="check"><input type="checkbox" name="debug_overlay_enabled"> <span data-i18n="settings.debugOverlay">Show debug window on every page</span></label>
|
||
<label class="check"><input type="checkbox" name="debug_gree_frames"> <span data-i18n="settings.debugGreeFrames">Include GREE protocol frames</span></label>
|
||
</div>
|
||
</section>
|
||
<section class="panel settings-block">
|
||
<div class="settings-block-head"><div><h3 data-i18n="settings.backup">Configuration backup</h3><p class="warning-note" data-i18n="settings.backupHint">Export/import application configuration. Exported files can contain GREE device keys plus Home Assistant and InfluxDB secrets; metrics and API access tokens are not included.</p></div></div>
|
||
<div class="form-actions backup-actions"><button type="button" class="secondary" id="exportSettings" data-i18n="settings.export">Export settings</button><button type="button" class="secondary" id="importSettings" data-i18n="settings.import">Import settings</button><input type="file" id="importSettingsFile" accept="application/json,.json" hidden></div>
|
||
</section>
|
||
<div class="panel system-panel" id="systemInfo"></div>
|
||
</div>
|
||
<div class="settings-pane" data-settings-pane="gree" hidden>
|
||
<section class="panel settings-block">
|
||
<div class="settings-block-head"><div><h3 data-i18n="settings.greeConnection">GREE connection</h3><p data-i18n="settings.greeConnectionHint">Controller identity, polling intervals and LAN discovery transport.</p></div></div>
|
||
<div class="settings-grid">
|
||
<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>
|
||
</div>
|
||
</section>
|
||
<section class="panel settings-block">
|
||
<div class="settings-block-head"><div><h3 data-i18n="settings.greeCommands">GREE commands</h3><p data-i18n="settings.suppressBeepHint">Only changed properties are sent. Buzzer suppression is also requested when supported by the unit firmware.</p></div></div>
|
||
<div class="settings-grid"><label class="check wide"><input type="checkbox" name="suppress_device_beep"> <span data-i18n="settings.suppressBeep">Try to suppress command beeps</span></label></div>
|
||
</section>
|
||
<section class="panel settings-block">
|
||
<div class="settings-block-head"><div><h3 data-i18n="settings.greeTraffic">GREE traffic</h3><p data-i18n="settings.greeTrafficHint">Live counters of UDP frames received from configured air conditioners since controller startup.</p></div></div>
|
||
<div id="greeFrameStats" class="gree-frame-stats"></div>
|
||
</section>
|
||
</div>
|
||
<div class="settings-save-bar"><span data-i18n="settings.saveHint">Save changes made in the blocks above.</span><button class="primary" type="submit" data-i18n="actions.save">Save</button></div>
|
||
</form>
|
||
</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 logs-toolbar"><div><strong data-i18n="logs.retention">Log retention</strong><small data-i18n="logs.retentionHint">Old event rows are removed automatically during maintenance.</small></div><label><span data-i18n="logs.level">Level</span><select id="logLevelFilter"><option value="all" data-i18n="logs.all">All</option><option value="error" data-i18n="logs.errors">Errors</option><option value="warn" data-i18n="logs.warnings">Warnings</option><option value="info" data-i18n="logs.info">Info</option></select></label><label><span data-i18n="logs.category">Category</span><select id="logCategoryFilter"><option value="all" data-i18n="logs.all">All</option><option value="device" data-i18n="nav.devices">Devices</option><option value="zone" data-i18n="nav.zones">Thermostats</option><option value="automation" data-i18n="nav.automations">Automations</option><option value="settings" data-i18n="nav.settings">Settings</option><option value="integration" data-i18n="logs.integrations">Integrations</option><option value="system" data-i18n="logs.systemOther">System/other</option></select></label><label><span data-i18n="logs.keepFor">Keep for</span><select id="logRetentionDays"><option value="7" data-i18n="logs.days7">7 days</option><option value="14" data-i18n="logs.days14">14 days</option><option value="30" data-i18n="logs.days30">30 days</option><option value="90" data-i18n="logs.days90">90 days</option><option value="180" data-i18n="logs.days180">180 days</option><option value="365" data-i18n="logs.days365">365 days</option></select></label><button class="secondary" id="saveLogRetention" data-i18n="actions.save">Save</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="groups"><span data-i18n="nav.groups">Groups</span><span>›</span></button>
|
||
<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="simulation"><span data-i18n="nav.simulation">Simulator</span><span>›</span></button>
|
||
<button data-go="night"><span data-i18n="nav.nightMode">Night mode</span><span>›</span></button>
|
||
<button data-go="homeassistant"><span data-i18n="nav.homeAssistant">Home Assistant / Sensors</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.nameProtocol">Name / protocol</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="copy-settings-row"><select id="copyZoneSource"><option value="" data-i18n="zones.copyFrom">Copy thermostat settings from…</option></select><button type="button" class="secondary" id="copyZoneSettings" data-i18n="zones.copySettings">Copy settings</button></div>
|
||
<label><span data-i18n="zones.modePolicy">Mode policy</span><select name="mode_policy"><option value="house" data-i18n="zones.followHouse">Follow global mode</option><option value="cool" data-i18n="zones.coolOnly">Cooling only</option><option value="heat" data-i18n="zones.heatOnly">Heating only</option></select></label><input type="hidden" name="setpoint" value="23">
|
||
<p class="field-note" data-i18n="zones.modePolicyHint">If this zone should use the global Heating/Cooling mode selected in GREE Controller, leave “Follow global mode” unchanged. Cooling only / Heating only forces a fixed mode for this zone.</p>
|
||
<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="text" inputmode="decimal" step="0.5" name="cool_comfort_setpoint" value="23"></label><label><span data-i18n="preset.sleep">Sleep</span><input type="text" inputmode="decimal" step="0.5" name="cool_sleep_setpoint" value="24.5"></label><label><span data-i18n="preset.away">Away</span><input type="text" inputmode="decimal" 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="text" inputmode="decimal" step="0.5" name="heat_comfort_setpoint" value="21"></label><label><span data-i18n="preset.sleep">Sleep</span><input type="text" inputmode="decimal" step="0.5" name="heat_sleep_setpoint" value="19"></label><label><span data-i18n="preset.away">Away</span><input type="text" inputmode="decimal" step="0.5" name="heat_away_setpoint" value="17"></label></div>
|
||
</details>
|
||
<div class="two"><div class="hysteresis-config"><label id="commonHysteresisField"><span data-i18n="zones.hysteresis">Hysteresis °C</span><input type="text" inputmode="decimal" step="0.1" min="0.1" max="5" name="hysteresis" value="0.6"></label><label class="check" data-i18n-title="zones.separateHysteresisHint" title="Use different hysteresis values for heating and cooling"><input type="checkbox" name="separate_hysteresis"> <span data-i18n="zones.separateHysteresis">Set heating and cooling hysteresis separately</span></label></div><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="separateHysteresisFields" class="two" hidden><label><span data-i18n="zones.coolHysteresis">Cooling hysteresis °C</span><input type="text" inputmode="decimal" step="0.1" min="0.1" max="5" name="cool_hysteresis" value="0.6"></label><label><span data-i18n="zones.heatHysteresis">Heating hysteresis °C</span><input type="text" inputmode="decimal" step="0.1" min="0.1" max="5" name="heat_hysteresis" value="0.6"></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="text" inputmode="decimal" 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="text" inputmode="decimal" 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"><input type="hidden" name="sensor_stale_after_seconds" value="300"><input type="hidden" name="revision" value="">
|
||
<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="temporaryThermostatDialog">
|
||
<form method="dialog" id="temporaryThermostatForm" class="dialog-form temporary-thermostat-form">
|
||
<input type="hidden" name="zone_id">
|
||
<div class="dialog-head"><div><span class="eyebrow" data-i18n="zones.quickThermostat">Szybki termostat</span><h2 data-i18n="zones.temporaryTitle">Włącz czasowo</h2></div><button type="button" data-close>×</button></div>
|
||
<div id="temporaryThermostatActive" class="temporary-active-summary" hidden>
|
||
<div><small id="temporaryThermostatActiveLabel" data-i18n="zones.temporaryActive">Aktywny tryb czasowy</small><strong id="temporaryThermostatActiveCountdown">—</strong><span id="temporaryThermostatActiveDetail"></span></div>
|
||
<button type="button" class="danger" id="temporaryThermostatStop" data-i18n="zones.temporaryStop">Zakończ teraz</button>
|
||
</div>
|
||
<label><span data-i18n="zones.temporaryTarget">Temperatura szybkiego termostatu</span><input type="number" name="target_temperature" min="8" max="30" step="0.5" required></label>
|
||
<label><span data-i18n="zones.temporaryStartRule">Kiedy uruchomić</span><select name="start_kind" id="temporaryStartKind">
|
||
<option value="now" data-i18n="zones.temporaryStartNow">Od razu</option>
|
||
<option value="delay" data-i18n="zones.temporaryStartDelay">Z opóźnieniem</option>
|
||
<option value="at" data-i18n="zones.temporaryStartAt">O konkretnej dacie i godzinie</option>
|
||
</select></label>
|
||
|
||
<div id="temporaryStartDelayFields" class="temporary-rule-fields" hidden>
|
||
<label><span data-i18n="zones.temporaryStartDelayMinutes">Uruchom za (min)</span><input type="number" name="start_delay_minutes" min="1" max="43200" value="30"></label>
|
||
<div class="temporary-presets"><button type="button" data-temporary-start-delay="15">15 min</button><button type="button" data-temporary-start-delay="30">30 min</button><button type="button" data-temporary-start-delay="60">1 h</button><button type="button" data-temporary-start-delay="120">2 h</button></div>
|
||
</div>
|
||
|
||
<div id="temporaryStartAtFields" class="temporary-rule-fields" hidden>
|
||
<label><span data-i18n="zones.temporaryStartAtDate">Uruchom o</span><input type="datetime-local" name="start_at"></label>
|
||
</div>
|
||
|
||
<label><span data-i18n="zones.temporaryFinishRule">Kiedy wrócić do automatyki</span><select name="finish_kind" id="temporaryFinishKind">
|
||
<option value="duration" data-i18n="zones.temporaryDuration">Po określonym czasie</option>
|
||
<option value="until" data-i18n="zones.temporaryUntil">O konkretnej dacie i godzinie</option>
|
||
<option value="temperature_reached" data-i18n="zones.temporaryReached">Po osiągnięciu warunku temperatury</option>
|
||
<option value="temperature_stable" data-i18n="zones.temporaryStable">Po utrzymaniu warunku temperatury</option>
|
||
<option value="schedule_boundary" data-i18n="zones.temporaryScheduleBoundary">Przy następnym przejściu harmonogramu</option>
|
||
</select></label>
|
||
|
||
<div id="temporaryDurationFields" class="temporary-rule-fields">
|
||
<label><span data-i18n="zones.temporaryDurationMinutes">Czas działania (min)</span><input type="number" name="duration_minutes" min="1" max="14400" value="60"></label>
|
||
<div class="temporary-presets"><button type="button" data-temporary-duration="30">30 min</button><button type="button" data-temporary-duration="60">1 h</button><button type="button" data-temporary-duration="120">2 h</button><button type="button" data-temporary-duration="240">4 h</button></div>
|
||
</div>
|
||
|
||
<div id="temporaryUntilFields" class="temporary-rule-fields" hidden>
|
||
<label><span data-i18n="zones.temporaryUntilDate">Wyłącz o</span><input type="datetime-local" name="until"></label>
|
||
</div>
|
||
|
||
<div id="temporaryTemperatureFields" class="temporary-rule-fields" hidden>
|
||
<div class="two"><label><span data-i18n="zones.temporaryCondition">Warunek</span><select name="temperature_operator">
|
||
<option value="within" data-i18n="zones.temporaryWithin">w zakresie celu ± tolerancja</option>
|
||
<option value="at_or_below" data-i18n="zones.temporaryAtOrBelow">temperatura ≤ cel</option>
|
||
<option value="at_or_above" data-i18n="zones.temporaryAtOrAbove">temperatura ≥ cel</option>
|
||
</select></label><label><span data-i18n="zones.temporaryTolerance">Tolerancja °C</span><input type="number" name="tolerance_c" min="0.1" max="3" step="0.1" value="0.3"></label></div>
|
||
<label id="temporaryHoldField"><span data-i18n="zones.temporaryHoldMinutes">Warunek musi trwać (min)</span><input type="number" name="hold_minutes" min="1" max="1440" value="60"></label>
|
||
<label><span data-i18n="zones.temporarySafetyLimit">Limit bezpieczeństwa (min, opcjonalnie)</span><input type="number" name="max_duration_minutes" min="1" max="14400" value="240"></label>
|
||
<p class="field-note" data-i18n="zones.temporaryStableHint">Jeśli temperatura wyjdzie poza warunek, licznik utrzymania zeruje się i zaczyna od nowa po ponownym wejściu w zakres.</p>
|
||
</div>
|
||
|
||
<div id="temporaryScheduleFields" class="temporary-rule-fields" hidden><p class="field-note" data-i18n="zones.temporaryScheduleHint">Szybki termostat zakończy się przy najbliższej zmianie aktywnego wpisu harmonogramu tej strefy.</p></div>
|
||
<div class="form-actions"><button type="button" class="secondary" data-close data-i18n="actions.cancel">Anuluj</button><button class="primary" type="submit" id="temporaryThermostatSubmit" data-i18n="zones.temporaryStart">Włącz czasowo</button></div>
|
||
</form>
|
||
</dialog>
|
||
|
||
<dialog id="groupDialog">
|
||
<form method="dialog" id="groupForm" class="dialog-form">
|
||
<input type="hidden" name="id">
|
||
<div class="dialog-head"><h2 data-i18n="groups.group">Group</h2><button type="button" data-close>×</button></div>
|
||
<label><span data-i18n="common.name">Name</span><input name="name" required data-i18n-placeholder="groups.namePlaceholder" placeholder="Upstairs"></label>
|
||
<fieldset><legend data-i18n="groups.members">Group members</legend><div class="group-zone-choices" id="groupZoneChoices"></div></fieldset>
|
||
<p class="field-note" data-i18n="groups.membersHint">Groups are built from thermostat zones. ON takes control of members; OFF powers their units down without leaving a group ownership lock.</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="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="text" inputmode="decimal" 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>
|
||
<div class="copy-settings-row"><select id="automationPreset"><option value="" data-i18n="automations.preset">Preset…</option><option value="hot" data-i18n="automations.presetHot">High temperature cooling</option><option value="cold" data-i18n="automations.presetCold">Low temperature heating</option><option value="morning" data-i18n="automations.presetMorning">Morning comfort</option><option value="nightoff" data-i18n="automations.presetNightOff">Night power off</option></select><button type="button" class="secondary" id="applyAutomationPreset" data-i18n="actions.apply">Apply</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="text" inputmode="decimal" 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="automations.targetType">Target type</span><select name="action_target_kind"><option value="device" data-i18n="common.device">Device</option><option value="group" data-i18n="groups.group">Group</option></select></label>
|
||
<label id="automationDeviceTarget"><span data-i18n="common.device">Device</span><select name="action_device_id"></select></label>
|
||
<label id="automationGroupTarget" hidden><span data-i18n="groups.group">Group</span><select name="action_group_id"></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>
|
||
<label id="automationGroupPreset" hidden><span data-i18n="groups.profile">Group profile</span><select name="action_preset"><option value="" data-i18n="actions.noChange">No change</option><option value="auto" data-i18n="preset.auto">Auto</option><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></select></label>
|
||
<div class="two"><label id="automationTargetTemperature"><span data-i18n="common.targetC">Target °C</span><input type="text" inputmode="decimal" 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>
|
||
<p class="field-note" id="automationGroupHint" hidden data-i18n="automations.groupHint">For a group, Auto mode means follow the whole-house heating/cooling mode. Group presets are applied to all member zones.</p>
|
||
<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>
|
||
|
||
<aside id="debugOverlay" class="debug-overlay" hidden>
|
||
<div class="debug-overlay-head">
|
||
<div class="debug-overlay-title"><strong data-i18n="debug.title">Live debug</strong><small id="debugOverlayStatus"></small></div>
|
||
<div class="debug-overlay-actions">
|
||
<div class="debug-source-tabs" role="tablist" data-i18n-aria="debug.filterAria" aria-label="Debug source">
|
||
<button type="button" class="active" role="tab" aria-selected="true" data-debug-filter="all" data-i18n="debug.all">All</button>
|
||
<button type="button" role="tab" aria-selected="false" data-debug-filter="requests" data-i18n="debug.requests">Requests</button>
|
||
<button type="button" role="tab" aria-selected="false" data-debug-filter="gree" data-i18n="debug.gree">GREE</button>
|
||
</div>
|
||
<button type="button" class="debug-clear" data-action="debug-clear" data-i18n="debug.clear">Clear</button>
|
||
</div>
|
||
</div>
|
||
<div id="debugOverlayLines" class="debug-overlay-lines"></div>
|
||
</aside>
|
||
<div id="toastStack" class="toast-stack" role="status" aria-live="polite" aria-atomic="false"></div>
|
||
<script src="__GREE_APP_ASSET__" defer></script>
|
||
</body>
|
||
</html>
|