Partials
A partial is a piece of template with a name. Write it once, and every widget that renders it draws it the same way. When you change the partial, every widget that uses it follows.
You render a partial with {% render %}, its name in quotes, and the values it needs:
{% render 'value-row', label: 'Power', value: cap.measure_power.text, unit: cap.measure_power.unit %}The values after the name are the partial’s parameters. A partial says which parameters it takes, what type each one is, and what it uses when a render leaves one out.
Partials that come with Tiledash
Two partials are built in, and every widget can render them.
value-row is one reading in a list: a label on the left, the value on the right with a smaller unit, and an optional icon.
| Parameter | Type | What it is |
|---|---|---|
label | text | What the value is. Required. |
value | any value | The value, a number or text. Required. |
unit | text | Its unit, shown smaller. |
icon | icon | An icon in front of the label. |
device-head is the row at the top of the built-in device widgets: an icon, the device’s name and room, and a chip at the end that says Unavailable when the device is, or any word you give it.
| Parameter | Type | What it is |
|---|---|---|
device | device | The device to show. The tile’s own device unless you pass another. |
chip | any value | A word at the end, such as a mode. |
name | text | Instead of the device’s name. |
icon | icon | Instead of the device’s icon. |
room | text | Instead of the device’s room. |
{% render 'device-head', chip: cap.thermostat_mode.value %}The partials reference lists them with an example each.
A widget that renders value-row for every reading of a device looks like this:
<h2 class="title">{{ device.name }}</h2><ul class="list"> {% for entry in cap %} {% assign reading = entry[1] %} <li> {% render 'value-row', label: reading.title, value: reading.text, unit: reading.unit %} </li> {% endfor %}</ul>The loop goes over cap, which holds every reading the device has.
Each round gives a pair, entry[0] for the reading’s id and entry[1] for the reading itself.
What a partial can see
A partial sees only what you give it.
It doesn’t see cap, settings, state or the variables you assigned in the widget, so pass what it needs:
{% render 'value-row', label: cap.measure_humidity.title, value: cap.measure_humidity.text %}The tile’s device
Only a widget for one device has a tile device: a light, a socket, a thermostat.
A clock, the weather, the energy tiles, a flow or mood button and every widget you start empty have none.
A partial gets the tile’s device when it declares a parameter called device, of the type device.
A render that leaves it out gives it the tile’s own device, or nothing in a widget without one.
device-head declares it, which is why a bare {% render 'device-head' %} shows the tile’s own device.
To show another device, or one in a widget without a tile device, pass your own device:, such as device: settings.pump.
A partial that doesn’t declare device sees it only when a render passes it, like any other name.
Partials saved before this worked the same way already read the tile’s device without declaring it: Tiledash declared device for them, so they draw as they did.
A few more rules:
- Anything a partial assigns stays inside it.
- The values you pass are plain values. A filter after one isn’t allowed, so
label: device.name | upcaseis an error. Assign it first:{% assign name = device.name | upcase %}and thenlabel: name. - A variable made with
letis passed like any other, and aletinside a partial stays inside it. - Expressions in a partial use the same clock as the widget’s.
- A partial can render another partial, up to three levels deep counting from the widget. A widget can render up to 12 different partials, the partials they render included.
- The name is always in quotes, and can’t come from a variable. It is a partial’s name of lower-case letters, digits and dashes, starting with a letter, or the id of a partial from the store, like
'@ann/sprinkler'.
A partial that doesn’t exist, doesn’t parse or goes too deep is an error when you save.
Parameters
A partial declares the parameters it takes, each with:
- a name, which the partial reads as
{{ name }}; - a type;
- a label, and a line of help if you like;
- whether it is required: every render has to pass it;
- a default, for an optional one: what the partial gets when a render leaves it out;
- an example, as Liquid, which the catalog and the autocomplete use:
cap.onoff.value,'Lawn'.
| Type | What a render passes | Example render value |
|---|---|---|
| yes/no | true or false, or a value that is one | on: cap.onoff.value |
| number | A number, within its lowest and highest if it has them | speed: 2 |
| text | Text in quotes, or a value that is text | zone: 'Front lawn' |
| choice | One of the choices it lists, in quotes | flow: 'strong' |
| colour | A theme colour, such as 'accent', or '#rrggbb' | tint: 'cool' |
| icon | An icon name | icon: 'device.sprinkler' |
| device | A device, such as device or settings.pump | device: settings.pump |
| any value | Whatever the render passes: a reading, text or a number | value: cap.measure_power.text |
A required parameter has no default.
An optional one without a default is empty when a render leaves it out, so {{ zone | default: device.name }} is a good way to read one.
The one exception is a device parameter called device: left out, it is the tile’s own device.
A render that passes nil, or a value that turns out empty, gets the default too.
What a render is checked for
When you save a widget, each render in it is checked against what its partial declares:
- leaving out a required parameter is an error;
- a value written out in the render that doesn’t fit its type is an error:
on: 'yes'for a yes/no, a choice that isn’t one of its choices, a number out of range; - passing a name the partial doesn’t declare is a warning, which usually means a typo.
Values that come from the device or a setting, like on: cap.onoff.value, are passed as they are: what they hold is only known when the tile draws.
A partial that declares no parameters isn’t checked this way.
Your own partials
Choose New partial on the Your widgets page.
Give it a name of 2 to 40 characters, lower-case letters, digits and dashes, starting with a letter, such as stat or my-header.
It starts empty, or as a copy of one of Tiledash’s partials.
The editor is the same one you use for widgets: a Template tab and a Style tab, a preview, a Catalog panel, History and Save. The partial’s Settings tab holds the line that describes it in the catalog, its Parameters, how to render it, and deleting it, once no widget of yours renders it.
On the Settings tab, Add a parameter asks for its type and opens it to fill in.
When the template reads a name you haven’t declared, the tab says so, with Declare next to it.
The Data panel lists the parameters with their type and default, and the + beside one puts {{ name }} in the template.
The preview draws the partial in each widget of yours that renders it, with the values that widget passes, and in A plain tile. In the plain tile you get a control for each parameter: a switch for a yes/no, buttons or a list for a choice, swatches for a colour, the icon picker, and fields for numbers and text. That is where you try what it draws in each state.
Here is a small partial, stat, with two text parameters, label required and value optional:
<div class="stat"> <span class="stat-label">{{ label }}</span> <span class="stat-value">{{ value | default: '—' }}</span></div>.stat { display: grid; gap: 2px;}.stat-label { font: 500 11px var(--hd-font-mono); color: var(--hd-text-muted);}.stat-value { font: var(--hd-number-weight) 24px var(--hd-font-display);}and a widget that renders it twice:
{% render 'stat', label: 'Humidity', value: cap.measure_humidity.text %}{% render 'stat', label: 'CO₂', value: cap.measure_co2.text %}In the widget editor
Typing {% render ' in a widget’s template lists every partial it can render: yours, those you installed from the store, and Tiledash’s.
After the name and a comma it lists the parameters, the required ones first, each with its type and default.
After name: it offers the values that fit: true and false for a yes/no, the choices of a choice, the theme’s colours, the icons, and then the widget’s own values.
Point at a partial’s name in a render to see what it is, where it comes from and what it takes. The checks above show under the code while you type, as well as when you save.
A partial’s style
A partial has a style of its own, and it is added to every widget that renders it, after the widget’s own style. Everything runs in the widget’s own Shadow DOM, so a rule in a partial can read the same theme variables as the widget does, and can’t leak into the page.
Start the names in a partial with the partial’s name, as value-row does with value-row-label.
A widget’s style and a partial’s style share one scope: a class called .label in both would apply to both, and two @keyframes spin would be one.
That goes for animation names too: @keyframes sprinkler-spin, not @keyframes spin.
For an animation, take the duration from the theme, so it calms down or stops when the theme lets less move.
A loop that should run slower than the theme’s slowest step can multiply it: calc(var(--hd-motion-slow) * 6) is still 0ms when nothing may move.
An example: a sprinkler that turns while it runs
A partial sprinkler with four parameters:
| Parameter | Type | Default |
|---|---|---|
on | yes/no | no |
zone | text | (empty) |
flow | choice: gentle, strong | gentle |
device | device | the tile’s device |
<div class="sprinkler sprinkler-{{ flow }}{% if on %} sprinkler-on{% endif %}"> <span class="sprinkler-icon"><hd-icon name="device.sprinkler" size="26px"></hd-icon></span> <span class="sprinkler-zone">{{ zone | default: device.name }}</span> <span class="sprinkler-state">{% if on %}Watering{% else %}Off{% endif %}</span></div>@keyframes sprinkler-spin { to { rotate: 1turn; }}.sprinkler { display: flex; align-items: center; gap: 12px;}.sprinkler-icon { display: grid; place-items: center; width: 44px; height: 44px; border-radius: 50%; background: var(--hd-surface-raised);}.sprinkler-on .sprinkler-icon { color: var(--hd-cool);}.sprinkler-on .sprinkler-icon hd-icon { animation: sprinkler-spin calc(var(--hd-motion-slow) * 6) linear infinite;}.sprinkler-on.sprinkler-strong .sprinkler-icon hd-icon { animation-duration: calc(var(--hd-motion-slow) * 3);}A widget for one valve renders it with whether that valve is on, and the sprinkler shows the tile’s device:
{% render 'sprinkler', on: cap.onoff.value, zone: 'Front lawn' %}A Garden widget you started empty, with a device setting for each valve, has no tile device, so it passes each valve:
{% render 'sprinkler', device: settings.front, on: settings.front.cap.onoff.value %}{% render 'sprinkler', device: settings.back, on: settings.back.cap.onoff.value %}Replacing a Tiledash partial
Name your partial like one of Tiledash’s, value-row for instance, and yours is used instead of it, in every widget that renders it.
That includes Tiledash’s own widgets: the car and the home battery render device-head, so a partial of yours with that name changes how they draw their head.
It is a quick way to change a look everywhere, and a risky one, so keep taking the parameters the original takes.
What is checked when you save
Saving a partial checks it on its own: with its parameters at their defaults, a demo light’s device as the tile’s when it declares device, and once more for each yes/no turned the other way, so both of what it draws are checked.
A name it reads without declaring it is a warning, device included.
It then checks every widget of yours that renders it, with all their demo states, as they will draw with the new version, their renders against the new parameters included.
An error in any of them stops the save, so saving a partial never breaks a widget that already works.
You can keep up to 30 partials, each with a template and a style of at most 20,000 characters, and up to 16 parameters.
Partials from the store
People share partials in the store, next to widgets and themes. Filter on Partials to see them. A partial’s page draws it in a plain tile with a control for each of its parameters, so you can try it before you install it.
Installing a partial doesn’t change anything yet. Your own widgets can then render it by its id, which is the publisher’s name and its own:
{% render '@ann/sprinkler', on: cap.onoff.value %}Its page and the toast after installing show the line to copy, and the widget editor’s autocomplete lists it. A partial of your own can’t have a name like that, so it can never stand in for one from the store.
Your widgets draw the version you installed. When a newer version is out, it shows under Installed like any other update:
- A new minor or patch version never breaks a render that fit the version before: it can’t remove or change a parameter, or add a required one.
- A new major version can. Before you move to it, Tiledash checks each widget of yours that renders it with the new version, and lists what would go wrong.
- You can go back to an earlier version, and let updates within the same major come by themselves.
A partial can’t be uninstalled while a widget of yours renders it: take it out of those widgets first. Installed partials are listed on the Your widgets page too, under From the store, with the version you have and which widgets render it.
Sharing your partial
Publish to store… at the bottom of the partial editor publishes your last save, with its parameters, as a new item or as the next version of one you published before. You can also publish it as a folder from a zip or with the command line: see publishing from the command line.
What the store checks before it publishes a partial:
- the same checks as saving it, at its defaults and with each yes/no both ways;
- it renders only Tiledash’s partials, not one of yours or another from the store;
- it doesn’t name a device by its id: it is for every Homey;
- what it loads from outside the Homey, which needs a reason, as for a widget;
- its class and animation names start with its name in the store, or you get a warning.
A version that removes or changes a parameter, adds a required one or makes an optional one required has to be a new major version: 2.0.0 after 1.4.0.
Widgets that render a store partial
A widget of yours can render a store partial you installed, as above. When you publish that widget, a copy of the partial at the version you have goes into the widget, so the people who install the widget don’t need the partial. The widget’s page in the store credits it under Uses.
Copying such a widget to your own widgets makes that copy one of your own partials, named after the widget, such as garden-sprinkler, and its renders follow the new name.