Embed modes

There are three ways to put widget cards on a page. Pick whichever suits your environment.

Custom element mode

Uses <hrv-card> and <hrv-group> as native HTML custom elements.

<script src="https://myhome.example.com/harvest_assets/harvest.min.js"></script>
<script>
  HArvest.config({ haUrl: "https://myhome.example.com", token: "hwt_..." });
</script>

<hrv-card entity="light.bedroom_main"></hrv-card>

<!-- Group example -->
<hrv-group>
  <hrv-card entity="light.living_room"></hrv-card>
  <hrv-card entity="light.dining_room"></hrv-card>
</hrv-group>

The script src points at your HA instance, which serves the widget bundle directly out of the integration's own files at /harvest_assets/harvest.min.js. The widget always matches the running integration version - update HArvest via HACS and every embedded widget picks up the new bundle on the next page load.

Data attribute mode

Uses plain <div> elements with data- attributes. Mostly useful in environments that sanitize custom HTML tags - some page builders, CMSes, or email templates won't allow unknown elements like <hrv-card>.

<script src="https://myhome.example.com/harvest_assets/harvest.min.js"></script>

<div class="hrv-group" data-token="hwt_..." data-ha-url="https://myhome.example.com">
  <div class="hrv-mount" data-entity="light.bedroom_main"></div>
</div>

hrv-mount.js (bundled into harvest.min.js) scans the page on load for .hrv-mount and .hrv-group divs and mounts card elements inside them automatically. A MutationObserver handles elements added dynamically after page load.

Programmatic mode

Creates cards via JavaScript using HArvest.create(). Useful when you're building a dynamic UI and want to control exactly when and where cards appear - for example, responding to user interactions or building a dashboard from fetched data.

HArvest.config({ haUrl: "https://myhome.example.com", token: "hwt_..." });

const card = HArvest.create({
  entity: "light.bedroom_main",
  targetId: "my-container",  // ID of the element to append into
});

// Or attach it yourself:
const card2 = HArvest.create({ entity: "sensor.outdoor_temp" });
document.getElementById("sidebar").appendChild(card2);

Token configuration levels

The token and ha-url connection values can be set at three levels. Cards inherit from the nearest level where a value is set - card attributes override group attributes, which override the page-level defaults. You don't have to use all three levels.

LevelHow to set itApplies to
PageHArvest.config({ token, haUrl })All cards on the page that don't have a closer override
Grouptoken and ha-url on <hrv-group>All cards inside that group element
Cardtoken and ha-url on <hrv-card>That specific card only

Resolution order: card attribute wins, then the nearest ancestor group, then HArvest.config(). A missing value after all three levels are checked causes the card to show a configuration error.

Page-level (one token for the whole page)

Call HArvest.config() once, put only the entity on each card. The wizard generates this pattern by default when all entities in the snippet belong to the same token.

<script>
  HArvest.config({ haUrl: "https://myhome.example.com", token: "hwt_..." });
</script>

<hrv-card entity="light.bedroom_main"></hrv-card>
<hrv-card entity="sensor.outdoor_temp"></hrv-card>

Group-level (multiple tokens on one page)

Use separate <hrv-group> elements when you have cards from different tokens on the same page. Each group carries its own connection settings and shares one WebSocket connection per token.

<hrv-group token="hwt_tokenA..." ha-url="https://myhome.example.com">
  <hrv-card entity="light.living_room"></hrv-card>
  <hrv-card entity="light.dining_room"></hrv-card>
</hrv-group>

<hrv-group token="hwt_tokenB..." ha-url="https://myhome.example.com">
  <hrv-card entity="climate.main_thermostat"></hrv-card>
</hrv-group>

Card-level (each card self-contained)

Put the connection details directly on each card. Useful when cards are dropped into different parts of a page independently - for example, through a CMS shortcode that renders one card at a time.

<hrv-card token="hwt_..." ha-url="https://myhome.example.com" entity="light.bedroom_main"></hrv-card>

Cards sharing the same token and ha-url always share one WebSocket connection regardless of how the values are provided - so card-level configuration doesn't create extra connections compared to page-level.

hrv-card attributes

Only connection-identifying attributes belong on the HTML element. All display configuration (theme, graph settings, companion entities, animations) is managed server-side per entity in the panel and pushed to the widget over WebSocket.

entity one of entity / alias required string
The Home Assistant entity ID to display. e.g. light.bedroom_main, sensor.outdoor_temperature. Use this or alias - not both. If both are present, entity takes priority and a console warning is logged.
alias one of entity / alias required string
An 8-character alias for the entity, as generated and shown in the panel. e.g. dJ5x3Apd. Use this or entity - not both. Aliases let you avoid putting real entity IDs in your page source. The server resolves the alias to the real entity ID during auth.
token optional string
Widget token ID. Overrides the value from HArvest.config() or a parent <hrv-group> for this specific card. Rarely needed - usually set once at the page or group level.
ha-url optional string
External HTTPS URL of the Home Assistant instance. Overrides the value from HArvest.config() or a parent group. e.g. https://myhome.duckdns.org.
token-secret optional string
HMAC signing secret. Required when the token has HMAC authentication enabled. The secret is used to sign auth messages client-side so the server can verify that auth requests came from a legitimate page embed.

hrv-group attributes

<hrv-group> is a context provider that passes connection settings to all descendant <hrv-card> elements. It doesn't render anything itself.

token optional string
Widget token ID, inherited by all child cards. Overrides the page-level value from HArvest.config().
ha-url optional string
HA instance URL, inherited by all child cards.
token-secret optional string
HMAC secret, inherited by all child cards.

JavaScript API

The widget exposes a global HArvest object with several methods and properties.

HArvest.config(options)

Sets page-level defaults inherited by all <hrv-card> and <hrv-group> elements on the page. This call is optional - if you set token and ha-url directly on your group or card elements you don't need it. Merges with prior calls - later calls override earlier values for the same key. Connection values are read lazily at WebSocket connection time, so call order relative to element mounting is not a concern.

HArvest.config({
  haUrl: "https://myhome.example.com",  // HA external URL
  token: "hwt_...",                     // default token for all cards
  tokenSecret: "my-hmac-secret",        // HMAC secret (if HMAC enabled)
  colorScheme: "auto",                  // "auto" | "light" | "dark"
});
OptionTypeNotes
haUrlstringPage-level default HA URL. Inherited by cards/groups that don't set their own.
tokenstringPage-level default token ID. Inherited by cards/groups that don't set their own.
tokenSecretstringHMAC signing secret. Only needed when the token has HMAC authentication enabled.
colorScheme"auto" | "light" | "dark"Page-level color scheme fallback. The token's server-side setting takes priority.

HArvest.create(config)

Programmatically creates an HrvCard element and appends it to a target container. Returns the element instance.

const card = HArvest.create({
  entity: "light.bedroom_main",
  targetId: "my-container",   // ID of the element to append into
  // haUrl and token can be specified here or via HArvest.config()
});

HArvest.getCard(entityId)

Returns the HrvCard instance currently registered for a given entity ID, or null if none exists. When multiple cards on the same page subscribe to the same entity, this returns the most recently registered one.

const card = HArvest.getCard("light.bedroom_main");
if (card) {
  console.log(card.entityId, card.lastState);
}

HArvest.registerRenderer(key, RendererClass)

Registers a custom renderer class for a given entity domain or device class. Overrides the built-in renderer globally for that key. Last call wins. See the Theming page for details.

HArvest.registerRenderer("light", MyCustomLightCard);
HArvest.registerRenderer("sensor.temperature", MyTempCard);  // device class specific
Commands require server-side registration

Registering a renderer only handles the display side. If your renderer sends commands (service calls) for a Tier 2 domain, the HA admin must also register that domain's allowed services in the panel under Settings > Custom Domains. Without this step, commands will be rejected with HRV_PERMISSION_DENIED. Tier 1 domains already have built-in service lists and do not need this step. See Entity types - Custom domains for details.

HArvest.renderers

An object exposing all built-in renderer classes so you can extend them:

// Available built-in renderers:
HArvest.renderers.BaseCard
HArvest.renderers.LightCard
HArvest.renderers.SwitchCard
HArvest.renderers.FanCard
HArvest.renderers.ClimateCard
HArvest.renderers.CoverCard
HArvest.renderers.MediaPlayerCard
HArvest.renderers.RemoteCard
HArvest.renderers.TemperatureSensorCard
HArvest.renderers.HumiditySensorCard
HArvest.renderers.BatterySensorCard
HArvest.renderers.GenericSensorCard
HArvest.renderers.BinarySensorCard
HArvest.renderers.InputBooleanCard
HArvest.renderers.InputNumberCard
HArvest.renderers.InputSelectCard
HArvest.renderers.HarvestActionCard
HArvest.renderers.TimerCard
HArvest.renderers.WeatherCard
HArvest.renderers.BadgeCard      // compact pill for badge capability
HArvest.renderers.GenericCard    // Tier 2 fallback

HArvest.track.anyState(callback)

Registers a callback that fires whenever any entity's state changes on the page. Useful for logging, analytics, or driving custom page logic based on entity state.

HArvest.track.anyState((entityId, state, attributes) => {
  console.log(`${entityId} changed to ${state}`);
});

Companion entities

Companions are secondary entities displayed as small status indicators inside a primary card. For example, a motion sensor shown inside a light card, or a lock status shown alongside a cover card.

Companions are configured server-side in the panel (entity detail screen, Display settings, Companion entities). They don't need any HTML attribute on the card element - the server automatically subscribes the companion entities and delivers them alongside the primary entity's definition.

Companion eligibility

Not every entity type can be a companion. Eligible domains:

DomainAccess in companion role
lightToggle on/off
switchToggle on/off
input_booleanToggle on/off
fanToggle on/off
binary_sensorRead-only state
sensorRead-only state/value
coverRead-only state/position
remoteRead-only power state

There is no per-card companion limit; the 50-entity token budget is the only constraint. Each companion has a "Read only" toggle (default: on). Read-only companions never trigger service calls regardless of the domain. If the primary card is set to "Read only", all its companions are forced to read-only. Badge entities cannot have companions.

Server-managed settings

Card settings are managed server-side per entity in the panel. They are delivered to the widget over WebSocket during authentication and update in real time when changed.

SettingConfigured in panelNotes
Name overrideEntity settings cardCustom display name, overrides HA friendly_name
Force color schemeEntity settings cardAuto, Light, or Dark per entity
Graph type (line/bar/none)Entity settings cardHistory graph for sensor, binary_sensor, input_number
Graph hoursEntity settings cardTime window, 1-168 hours
Graph periodEntity settings cardAggregation period in minutes
Animate fan iconEntity settings cardSpinning fan when entity is on
Fan display modeEntity settings cardAuto, On/Off, Continuous, Stepped, or Cycle
Light feature togglesEntity settings cardShow/hide brightness, color temp, RGB controls
Climate feature togglesEntity settings cardShow/hide HVAC modes, presets, fan mode, swing mode
Cover feature togglesEntity settings cardShow/hide position slider, tilt control
Media player feature togglesEntity settings cardShow/hide transport, volume, source selector
Input number display modeEntity settings cardSlider or Buttons
Companion entitiesEntity settings cardSee above
Excluded attributesEntity settings cardLinked with feature toggles bidirectionally
Theme / CSS variablesEntities tab, theme cardPushed on connect and on theme change
LanguagePreferences tabBCP 47 tag or "auto"
Accessibility modePreferences tab"standard" or "enhanced"
On offline behaviorPreferences tab"dim", "hide", "message", or "last-state"
On error behaviorPreferences tab"dim", "hide", or "message"
Offline message textPreferences tabCustom text shown when entity is offline
Error message textPreferences tabCustom text shown on connection error

All per-entity settings are stored as display_hints on the entity access configuration. Feature toggles and excluded attributes are bidirectionally linked: disabling a feature toggle automatically excludes the corresponding attribute, and vice versa.

Tracking state events

The HArvest.track.anyState() callback receives every state change for every entity on the page. For more targeted tracking, listen on a specific card instance:

// The hrv-card element must be in the DOM before attaching a listener
document.querySelector("hrv-card[entity='light.bedroom']")
  .addEventListener("hrv-state-change", (event) => {
    const { entityId, state, attributes } = event.detail;
    console.log(entityId, state);
  });

Offline and error states

When a widget can't reach HA or an entity is unavailable, the card enters an offline or error state. The behavior is configurable per token:

SettingOn offline behavior
last-state (default)Card shows the last known state, dimmed, with a stale indicator
dimCard dims but shows no explicit message
hideCard collapses to zero height
messageCard shows the offline message text (configurable per token)

The on-error setting (for WebSocket or auth errors, not entity-unavailable) follows the same options but without last-state.

Mobile and background tabs

Mobile browsers suspend background tabs and close WebSocket connections after a few seconds. When you switch back to the page, HArvest detects the tab is visible again and reconnects immediately, bypassing the normal backoff delay. During brief disconnects (under 30 seconds), cards continue showing their cached state without any stale indicator. If the disconnect lasts longer than 30 seconds, the "Last known state" banner appears until the connection is restored.