Skip to content

Commit f172a5e

Browse files
authored
Added click handlers and examples (#207)
Solves #72 Done instead of #172
1 parent 5f5f802 commit f172a5e

File tree

5 files changed

+114
-13
lines changed

5 files changed

+114
-13
lines changed

readme.md

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -696,7 +696,55 @@ entities:
696696
- map_y: y + vars.temp1.ys[i]
697697
```
698698

699-
### Universal functions
699+
### Entity click handlers
700+
701+
When the legend is clicked (or doubleclicked), the trace will be hidden (or showed alone) by default. This behaviour is controlled by [layout-legend-itemclick](https://plotly.com/javascript/reference/layout/#layout-legend-itemclick).
702+
On top of that, a `$fn` function can be used to add custom behaviour.
703+
If a handler returns false, the default behaviour trace toggle behaviour will be disabled, but this will also inhibit the `on_legend_dblclick ` handler. Disable the default behaviour via layout-legend-itemclick instead if you want to use both click and dblclick handlers.
704+
705+
```yaml
706+
type: custom:plotly-graph
707+
entities:
708+
- entity: sensor.temperature1
709+
on_legend_click: |-
710+
$fn () => (event_data) => {
711+
event = new Event( "hass-more-info")
712+
event.detail = { entityId: 'sensor.temperature1' };
713+
document.querySelector('home-assistant').dispatchEvent(event);
714+
return false; // disable trace toggling
715+
}
716+
```
717+
718+
Alternatively, clicking on points of the trace itself.
719+
720+
```yaml
721+
type: custom:plotly-graph
722+
entities:
723+
- entity: sensor.temperature1
724+
on_click: |-
725+
$fn () => (event_data) => {
726+
...
727+
// WARNING: this doesn't work and I don't understand why. Help welcome
728+
}
729+
```
730+
731+
There is also a double click plot handler, it works on the whole plotting area (not points of an entity). Beware that double click also autoscales the plot.
732+
733+
```yaml
734+
type: custom:plotly-graph
735+
entities:
736+
- entity: sensor.temperature1
737+
on_dblclick: |-
738+
$fn ({ hass }) => () => {
739+
hass.callService('light', 'turn_on', {
740+
entity_id: 'light.portique_lumiere'
741+
})
742+
}
743+
```
744+
745+
See more in plotly's [official docs](https://plotly.com/javascript/plotlyjs-events)
746+
747+
## Universal functions
700748

701749
Javascript functions allowed everywhere in the yaml. Evaluation is top to bottom and shallow to deep (depth first traversal).
702750

src/filters/fft-regression.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ export default class FFTRegression extends BaseRegression {
2525
const sorted = Array.from(re.data)
2626
.map((x, i) => [x, i])
2727
.sort((a, b) => b[0] - a[0]);
28-
console.log(`sorted`, sorted);
2928

3029
for (let i = degree; i < sorted.length; i++) {
3130
re.set(sorted[i][1], 0);

src/parse-config/defaults.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,15 @@ import { Config, InputConfig } from "../types";
33
import { parseColorScheme } from "./parse-color-scheme";
44
import { getEntityIndex } from "./parse-config";
55
import getThemedLayout, { HATheme } from "./themed-layout";
6-
6+
const noop$fn = () => () => {};
77
const defaultEntityRequired = {
88
entity: "",
99
show_value: false,
1010
internal: false,
1111
time_offset: "0s",
12+
on_legend_click: noop$fn,
13+
on_legend_dblclick: noop$fn,
14+
on_click: noop$fn,
1215
};
1316
const defaultEntityOptional = {
1417
mode: "lines",
@@ -59,6 +62,7 @@ const defaultYamlRequired = {
5962
yaxes: {},
6063
},
6164
layout: {},
65+
on_dblclick: noop$fn,
6266
};
6367

6468
//

src/plotly-graph-card.ts

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ export class PlotlyGraph extends HTMLElement {
5050
relayoutListener?: EventEmitter;
5151
restyleListener?: EventEmitter;
5252
refreshTimeout?: number;
53+
legendItemClick?: EventEmitter;
54+
legendItemDoubleclick?: EventEmitter;
55+
dataClick?: EventEmitter;
56+
doubleclick?: EventEmitter;
5357
} = {};
5458

5559
constructor() {
@@ -161,6 +165,18 @@ export class PlotlyGraph extends HTMLElement {
161165
"plotly_restyle",
162166
this.onRestyle
163167
)!;
168+
this.handles.legendItemClick = this.contentEl.on(
169+
"plotly_legendclick",
170+
this.onLegendItemClick
171+
)!;
172+
this.handles.legendItemDoubleclick = this.contentEl.on(
173+
"plotly_legenddoubleclick",
174+
this.onLegendItemDoubleclick
175+
)!;
176+
this.handles.doubleclick = this.contentEl.on(
177+
"plotly_doubleclick",
178+
this.onDoubleclick
179+
)!;
164180
this.resetButtonEl.addEventListener("click", this.exitBrowsingMode);
165181
this.touchController.connect();
166182
this.plot({ should_fetch: true });
@@ -170,6 +186,16 @@ export class PlotlyGraph extends HTMLElement {
170186
this.handles.resizeObserver!.disconnect();
171187
this.handles.relayoutListener!.off("plotly_relayout", this.onRelayout);
172188
this.handles.restyleListener!.off("plotly_restyle", this.onRestyle);
189+
this.handles.legendItemClick!.off(
190+
"plotly_legendclick",
191+
this.onLegendItemClick
192+
)!;
193+
this.handles.legendItemDoubleclick!.off(
194+
"plotly_legenddoubleclick",
195+
this.onLegendItemDoubleclick
196+
)!;
197+
this.handles.dataClick!.off("plotly_click", this.onDataClick)!;
198+
this.handles.doubleclick!.off("plotly_doubleclick", this.onDoubleclick)!;
173199
clearTimeout(this.handles.refreshTimeout!);
174200
this.resetButtonEl.removeEventListener("click", this.exitBrowsingMode);
175201
this.touchController.disconnect();
@@ -261,6 +287,27 @@ export class PlotlyGraph extends HTMLElement {
261287
await this.plot({ should_fetch: true });
262288
});
263289
};
290+
onLegendItemClick = ({ curveNumber, ...rest }) => {
291+
return this.parsed_config.entities[curveNumber].on_legend_click({
292+
curveNumber,
293+
...rest,
294+
});
295+
};
296+
onLegendItemDoubleclick = ({ curveNumber, ...rest }) => {
297+
return this.parsed_config.entities[curveNumber].on_legend_dblclick({
298+
curveNumber,
299+
...rest,
300+
});
301+
};
302+
onDataClick = ({ points, ...rest }) => {
303+
return this.parsed_config.entities[points[0].curveNumber].on_click({
304+
points,
305+
...rest,
306+
});
307+
};
308+
onDoubleclick = () => {
309+
return this.parsed_config.on_dblclick();
310+
};
264311
onRestyle = async () => {
265312
// trace visibility changed, fetch missing traces
266313
if (this.isInternalRelayout) return;
@@ -346,7 +393,6 @@ export class PlotlyGraph extends HTMLElement {
346393
.map((e) => "<span>" + (e || "See devtools console") + "</span>")
347394
.join("\n<br />\n");
348395
this.parsed_config = parsed;
349-
console.log("fetched", this.parsed_config);
350396

351397
const { entities, layout, config, refresh_interval } = this.parsed_config;
352398
clearTimeout(this.handles.refreshTimeout!);
@@ -364,6 +410,11 @@ export class PlotlyGraph extends HTMLElement {
364410
await Plotly.react(this.contentEl, entities, layout, config);
365411
this.contentEl.style.visibility = "";
366412
});
413+
// this.handles.dataClick?.off("plotly_click", this.onDataClick)!;
414+
this.handles.dataClick = this.contentEl.on(
415+
"plotly_click",
416+
this.onDataClick
417+
)!;
367418
});
368419
// The height of your card. Home Assistant uses this to automatically
369420
// distribute all cards over the available columns.

src/types.ts

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ export type InputConfig = {
2828
statistic?: StatisticType;
2929
period?: StatisticPeriod | "auto" | AutoPeriodConfig;
3030
unit_of_measurement?: string;
31-
lambda?: string;
3231
internal?: boolean;
3332
show_value?:
3433
| boolean
@@ -38,11 +37,15 @@ export type InputConfig = {
3837
offset?: TimeDurationStr;
3938
extend_to_present?: boolean;
4039
filters?: (Record<string, any> | string)[];
40+
on_legend_click?: Function;
41+
on_legend_dblclick?: Function;
42+
on_click?: Function;
4143
} & Partial<Plotly.PlotData>)[];
4244
defaults?: {
4345
entity?: Partial<Plotly.PlotData>;
4446
yaxes?: Partial<Plotly.Layout["yaxis"]>;
4547
};
48+
on_dblclick?: Function;
4649
layout?: Partial<Plotly.Layout>;
4750
config?: Partial<Plotly.Config>;
4851
ha_theme?: boolean;
@@ -54,14 +57,6 @@ export type InputConfig = {
5457

5558
export type EntityConfig = EntityIdConfig & {
5659
unit_of_measurement?: string;
57-
lambda?: (
58-
y: YValue[],
59-
x: Date[],
60-
raw_entity: ((StatisticValue | HassEntity) & {
61-
timestamp: number;
62-
value: any;
63-
})[]
64-
) => YValue[] | { x?: Date[]; y?: YValue[] };
6560
internal: boolean;
6661
show_value:
6762
| boolean
@@ -71,6 +66,9 @@ export type EntityConfig = EntityIdConfig & {
7166
offset: number;
7267
extend_to_present: boolean;
7368
filters: FilterFn[];
69+
on_legend_click: Function;
70+
on_legend_dblclick: Function;
71+
on_click: Function;
7472
} & Partial<Plotly.PlotData>;
7573

7674
export type Config = {
@@ -87,6 +85,7 @@ export type Config = {
8785
minimal_response: boolean;
8886
disable_pinch_to_zoom: boolean;
8987
visible_range: [number, number];
88+
on_dblclick: Function;
9089
};
9190
export type EntityIdStateConfig = {
9291
entity: string;

0 commit comments

Comments
 (0)