AC Defender Defender Dispatch · field notes for a real thermostat
Newsroom edition · operational documentation, updated with each release
Language & voice

Choose English, playful Hong Kong Cantonese, or bilingual labels. Funny levels style the surrounding voice; safety facts never change.

These preferences stay in this browser. Article names, dates, commands, and safety boundaries remain factual.
Back to home

AC Defender Dispatch · Field report

Deployment

The thermostat that holds the line — Home Assistant companion.

Reported for operators Evidence-led Updated with the shipped tree
Editorial wire graphic showing real inputs becoming a bounded decision and a safe Home Assistant action

Deployment

The app is designed to run in Docker Compose.

Compose

docker compose up -d --build

For a published release, load the archive matching the host architecture and use the exact image reference recorded in its .metadata.json companion. The release publishes independent linux/amd64 and linux/arm64 docker save archives because a single-platform archive is what docker load can consume:

gunzip ac-defender-docker-<version>-arm64.tar.gz
docker load --input ac-defender-docker-<version>-arm64.tar
export AC_DEFENDER_IMAGE=ac-defender:<version>-arm64
export AC_DEFENDER_VERSION=<version>
export AC_DEFENDER_REVISION=<commit>
docker compose up -d --no-build

The image carries OCI org.opencontainers.image.version and .revision labels. The Compose service has a 768 MiB memory limit, a 1.50 CPU limit, a 64 MiB no-execute temporary filesystem, and a read-only application root. /data and /app/App_Data are the only write mounts, so defender state, settings history, authentication state, and data-protection keys survive an image replacement without putting credentials in the image.

GET /healthz is anonymous and returns only status, release version, source revision, and the effective request scheme. It is the Compose healthcheck target and never returns Home Assistant URLs, access tokens, accounts, thermostat readings, or settings. A reverse proxy may forward HTTPS only after the operator configures all three bounded settings below; an empty trusted-proxy/network list ignores forwarded headers rather than trusting every caller:

FORWARDED_HEADERS_KNOWN_PROXIES=10.0.0.5
FORWARDED_HEADERS_KNOWN_IP_NETWORKS=10.0.0.0/24
FORWARDED_HEADERS_ALLOWED_HOSTS=ac-defender.example

scripts/deploy-host.sh is a non-publishing host-side path for a previously built archive. It requires Docker Compose v2, a mode-600 .env containing the three required Home Assistant keys, one exact allowed-host list, one or more exact trusted proxy IP/CIDR entries, an image checksum sidecar, a matching exact-schema metadata sidecar, an exact image/version/revision selection, a matching host architecture, and an immutable public release tag whose GitHub asset digest matches the archive independently of the adjacent checksum file. Set AC_DEFENDER_RELEASE_TAG to the published v0.1.* tag for the fixed public project identity Ding-Ding-Projects/HomeAssistantAcDefender. The project identity cannot be overridden by deployment environment variables. The script verifies image labels and /healthz, and independently verifies a rollback’s image, prior metadata, health status, and /healthz response. Run it with /s (or --silent) for unattended operation; no .env values or tokens are printed. The host must provide curl and python3 for public release lookup and strict duplicate-key/schema parsing. A missing release digest, unavailable strict parser, or unavailable previous image is a failed/unavailable deployment state, never a guessed success.

The compose file publishes:

8888:8080

The website is reachable at:

http://<host>:8888

Runtime State

Runtime state is bind-mounted from the deployment host so a no-cache rebuild, a Compose project-name change, or a fresh image cannot hide the existing settings behind a new empty named volume.

Inside the container:

/data/defender-state.json
/data/thermostat-history.jsonl
/data/settings-repo
/app/App_Data

On the host, those paths come from:

./App_Data/defender -> /data
./App_Data/auth     -> /app/App_Data

/data/settings-repo is a local git repository managed by the app. It stores the website target, defender switch, Settings page values, and schedule history only. It does not store Home Assistant tokens, accounts, DataProtection keys, .env, raw runtime telemetry, or thermostat history.

Secrets

Home Assistant credentials and tokens belong in .env on the deployment host. They must not be committed to Git.

Configuration reference (environment variables)

Required:

# Development-only loopback placeholder; use the real HTTPS API URL in production.
HomeAssistant__BaseUrl=http://127.0.0.1:8123
HomeAssistant__AllowInsecurePrivateNetworkHttp=false
HomeAssistant__EntityId=climate.dining_room
HomeAssistant__AccessToken=replace-with-token

Non-loopback Home Assistant traffic requires HTTPS. The only HTTP compatibility exception is an explicit HomeAssistant__AllowInsecurePrivateNetworkHttp=true setting for an RFC1918, link-local, or .local private target. This sends the bearer token over cleartext on that private network and must not be used for public hosts. Public HTTP remains rejected even when the compatibility switch is enabled. The deployment script validates this contract before loading an image.

The host .env also needs an exact reverse-proxy trust boundary:

FORWARDED_HEADERS_KNOWN_PROXIES=127.0.0.1
FORWARDED_HEADERS_KNOWN_IP_NETWORKS=
FORWARDED_HEADERS_ALLOWED_HOSTS=localhost

Optional Home Assistant entities:

HomeAssistant__WeatherEntityId=weather.home
HomeAssistant__OutdoorTemperatureEntityId=sensor.outdoor_temperature
HomeAssistant__OpenMeteoBackupEnabled=true
HomeAssistant__OpenMeteoLatitude=
HomeAssistant__OpenMeteoLongitude=
HomeAssistant__OpenMeteoRefreshMinutes=30
HomeAssistant__UsagePowerEntityId=sensor.alectra_hui_current_power
HomeAssistant__UsageEnergyEntityId=sensor.alectra_hui_energy_today
HomeAssistant__UsageCostEntityId=sensor.alectra_hui_cost_today
HomeAssistant__UsageHourlyCostEntityId=sensor.alectra_hui_hourly_cost
HomeAssistant__UsageCurrentBillEntityId=sensor.alectra_hui_current_bill
HomeAssistant__UsageCurrentBillDueEntityId=sensor.alectra_hui_current_bill_due
HomeAssistant__UsageCurrentBillStatusEntityId=sensor.alectra_hui_current_bill_status
HomeAssistant__Username=optional-bookkeeping-only
HomeAssistant__Password=optional-bookkeeping-only

If WeatherEntityId is blank the app discovers the first weather.* entity; with no weather entity, OutdoorTemperatureEntityId can still provide outdoor temperature. The usage entities come from the Alectra Hui integration; AC Defender only reads them once Home Assistant has created them, and historical usage needs the entity recorded by the recorder (api/history/period).

The Open-Meteo backup is enabled by default and is used only when Home Assistant cannot supply the corresponding real outdoor condition or forecast. If latitude and longitude are blank, AC Defender reads the installation coordinates from Home Assistant’s authenticated /api/config endpoint and caches them. Set both coordinate values to override that location, or set OpenMeteoBackupEnabled=false to disable external weather calls. An incomplete one-coordinate override is ignored as a pair so two different locations can never be combined. Before the public request, the location is rounded to two decimal places (roughly kilometre-scale) so exact household coordinates never leave the Home Assistant client. The refresh interval is clamped to at least 10 minutes; one request returns the current temperature_2m / weather_code and 48 hourly forecast points, so the default cadence stays far below the free non-commercial API limit. The Open-Meteo client is separate from the Home Assistant client and never receives the Home Assistant access token. Weather data is provided by Open-Meteo under CC BY 4.0; AC Defender maps WMO weather codes to display labels but does not otherwise alter the temperature forecast.

The key-free endpoint is intended for non-commercial use and has no uptime guarantee; commercial deployments should review Open-Meteo’s current plan and terms before enabling it. A failed backup request never creates synthetic weather, and AC Defender continues retrying on its throttled cadence.

Existing deployments with a non-loopback HTTP Home Assistant URL or a broadly permissioned .env will intentionally fail the new deployment preflight. The operator must either move the Home Assistant endpoint to HTTPS or explicitly choose the bounded private-LAN compatibility setting and repair the file ownership/mode; this project does not silently add a bypass or rewrite live host values.

Any Defender option from appsettings.json can be overridden the same way, e.g. Defender__RivalScheduleWatchEnabled=false or Defender__AcEstimatedAmps=20. See Energy & Costs for the electricity/TOU keys and Defender Logic for the guard keys.

When a Home Assistant climate state includes context, the defender stores it with the reading and the audit log: a user_id means a Home Assistant user/phone change, a parent_id means an automation/script/service chain, and a context ID without either is a thermostat/device-origin change. This attribution powers Super Defender, Remote Settling, the Desired-State Enforcer, and Rival Schedule Watch.

Failure modes

If Deployment cannot obtain one of its required real inputs, it reports a blocked, held, or unavailable result and leaves the background worker’s Home Assistant refresh running. It never fills a missing room reading, audit event, weather sample, usage value, or device state with a simulator value. If a real Home Assistant climate command is rejected, AC Defender records only the bounded operation identity and HTTP status. It does not retain request headers, payloads, upstream response bodies, or tokens. Direct controls show one persistent, bottom-corner outcome and keep identical-command backoff in place rather than stacking a retry notification or re-sending the command.

Security considerations

This feature consumes only the configured Home Assistant entity data, local settings, and the audit context named above. Tokens and credentials stay in the server environment; the static documentation site does not collect analytics, transmit search text, or embed third-party assets. Logs and exports should be reviewed before sharing because real entity names and timestamps can identify a household.

Verification

Verify the shipped behavior at the feature’s live page or endpoint, then run the repository’s documented build and test commands. Confirm the real-input and real-error paths, keyboard access, reduced-motion behavior, and a 390 px viewport without horizontal overflow. Record the exact commit and workflow result when publishing a release; a static screenshot alone is not proof of a live Home Assistant command.

Suggested articles

  • Feature briefs — find every documented surface and guard.
  • Defender Logic — follow the complete decision cycle and its bypass rules.
  • Settings — inspect persisted configuration, language modes, and safety limits.

Suggested next reads

Keyboard desk

Find a feature

Search every docs destination. Press Escape to close; plain text is the default and the builder is available beside this field.

0 destinations shown

APIOpen the documented feature brief. AccessibilityKeyboard, screen-reader, reduced-motion, contrast, and narrow-layout behavior across the hosted app and documentation. Alectra Peak Power SaverMakes the defender more chill and resource-saving when Alectra Hui reports on-peak, high price, or high power use. AlgorithmsSearch every AC Defender algorithm and open the full wiki article for each one. App tabsOpen the documented feature brief. Appearance editorOpen the documented feature brief. ArchitectureOpen the documented feature brief. Changelog viewerOpen the documented feature brief. Comfort BudgetCaps how many safe corrections happen inside a rolling window so the AC is not constantly nudged. Comfort CompromiseBlends a repeated wall choice into a temporary target, then fades it back to the website target. Comfort EnvelopeLets a tiny safe wall preference rest for a while instead of being corrected the instant it appears. Comfort MemoryLearns a small time-of-day target bias from repeated safe wall choices and re-applies it later that hour. Comfort PaceThe high-frequency planner: under heavy wall fighting it waits for a calm weather, sensor, or clock-aligned slot. Comfort Sync (quiet recovery)Spaces out and softens corrections so a fixed thermostat does not look like an instant robot. Command CamouflageGives a recent helper command time to look normal before another safe correction appears. Command paletteOpen the documented feature brief. Conflict QuietStands the defender down during an obvious tug-of-war over the thermostat. Context menus and mobile tab editingOpen the documented feature brief. Cool Mode RestorePuts the thermostat back into cool mode whenever someone switches it to heat/off/auto. Cool-Outdoor Shutdown (Open-Window Armistice)When it is genuinely cool outside and the forecast says it stays cool, the defender turns the AC fully off — and turns it back on by itself when the weather or the room demands it. Cooler Intent Fast LaneWhen people keep dialing the wall cooler, it skips quiet waits so the room cools sooner. Cooling Failure WatchRaises a repeating MEGA alert when cool mode is demanded but the AC is not really cooling, escalates to a full-site OMEGA alert when a rising room confirms it, then turns the AC off only for that confirmed failure until the room warms 0.5 C. Cooling RunwayGives the AC time to work after cooling starts before nudging the setpoint again. Defender LogicThe searchable decision cycle and exact rule text for every AC Defender algorithm. DeploymentOpen the documented feature brief. Desired-State EnforcerMakes the owner's chosen AC state win automatically: if someone else turns the unit off or moves the setpoint, it restores the exact desired state and keeps it there. Dim-sum startup surpriseOpen the documented feature brief. Dynamic CooldownA frequency-based quiet period after a manual thermostat change. Emergency ProtocolsOne-tap stand-down modes for too-cold, someone-upset, and suspicion situations. Energy & CostsOpen the documented feature brief. Every Guard, Explained SimplyA searchable plain-language guide to every AC Defender algorithm. Fan Energy SaverOptionally moves the fan to an energy-saving mode when the room is near target. Field Kitchen (food rations)Banks unspent AC dollars during siestas and cool-outdoor shutdowns, and spends them on forecast-hot days so the monthly budget eases exactly when cooling matters most. Front-door Guard PostPauses the defender and can turn the thermostat off when a real front-door person detector trips. HVAC AlibiWaits for a real HVAC action transition so a safe correction lands near a normal thermostat event. Heat, Pain & Survival FactsSourced hearing, heat-illness, and immediate personal-safety facts beside the playful Yell-O-Meter. HomeOpen the documented feature brief. Human NudgeMakes the final safe setpoint command look like a normal thermostat step instead of a precise bot number. Line counts and release archivesOpen the documented feature brief. Manual Comfort GraceLeaves a manual wall change alone while the room still feels comfortable. Natural CadencePicks a variable future slot for safe nudges so they never land at identical, robotic times. Natural WalkbackWalks a safe-band correction toward target in small, slightly random steps instead of one obvious jump. Notification historyOpen the documented feature brief. Regex search builderOpen the documented feature brief. Release operationsOpen the documented feature brief. Remote Settling GuardGives repeated phone/Home Assistant or automation thermostat changes a quiet settling window before a safe answer-back. Repeat QuietWaits before sending the very same thermostat number again. Repeated-Raise SurrenderIf a person re-raises the setpoint to about the same value 3+ times in 30 minutes, the defender adopts their number for 4 hours — the human wins the argument. Rival Schedule WatchKnows the AC vendor app's own temperature schedule (SLEEP / DEEP SLEEP / GOOD MORNING) and defends my temp when a scheduled block pushes the wall warmer while everyone sleeps. Room Trend GuardKeeps observing when the room is already stable or cooling after a wall change. Routine TimingLines safe corrections up with a normal-looking comfort-check rhythm instead of firing instantly. Schedule & Weather RulesTime-of-day target rules, each gated by a weather activation condition. Sensor RhythmTimes nudges to just after the normal Home Assistant reading beat so they look less mechanical. Setpoint EchoWaits for Home Assistant to report back the last setpoint before sending another safe command. Setpoint StillnessWaits until the wall setpoint stops moving before a safe correction answers back. SettingsOpen the documented feature brief. Settings history filters and exportsOpen the documented feature brief. Siesta Watch (mess hall)Lets the whole guard force nap on command; while they sleep the AC eases off and the money it would have spent is banked as food rations. Stealth GovernorRuns a whole-system low-profile hold when wall touches, noticed corrections, remote changes, and helper commands make the defender look too busy. Super DefenderDetects repeated phone/Home Assistant thermostat changes and tightens correction timing without cutting thermostat Wi-Fi. Tamper TruceIf the thermostat vanishes right after a correction exchange, assume a frustrated person detached it — stand down 2 hours instead of escalating. Telemetry AlibiWaits for a normal Home Assistant/weather/usage update before a safe correction, so the nudge is not an isolated event. Thermal MomentumWaits when the room is already cooling fast enough to reach target soon on its own. Thermostat OFF super-confirmationOpen the documented feature brief. Touch IntentReads whether recent wall changes trend warmer, cooler, or mixed, and extends grace for a clear warmer pattern. Touch SignatureMatches safe nudges to the size of steps people actually use on the wall thermostat. Tug-of-War TruceCalls a temporary truce when the real thermostat bounces up and down, so answer-back commands do not look like a duel. Upstairs Comfort GuardPrioritizes cooling when upstairs rooms get hot while someone is home. Visibility GuardSlows the next safe nudge when a wall touch lands right after a defender command (someone likely noticed). Wake-Up Truce (door sensor)A bedroom door opening at dawn means that person is awake — adopt the warm truce temperature before they ever touch the thermostat. Wall SettlingWaits for someone who is still tapping the wall thermostat to stop before correcting. Weather Drift TimingTimes safe corrections to real outdoor-weather movement instead of firing immediately. Website DebounceBlocks repeated website button taps for two minutes so the UI does not spam Home Assistant. Website TourOpen the documented feature brief. Windows Electron controllerOpen the documented feature brief. Windows HTTPS update-feed contractOpen the documented feature brief. Yelling PredictionsEvery Yell-O-Meter bill band, possible household consequence, uncertainty boundary, and factual input. Yelling Survival GuideA practical before, during, and after playbook for the Yell-O-Meter, with honest limits and immediate-safety routes.