diff --git a/readme.md b/readme.md
index f11b318..4aeef8f 100644
--- a/readme.md
+++ b/readme.md
@@ -696,7 +696,55 @@ entities:
- map_y: y + vars.temp1.ys[i]
```
-### Universal functions
+### Entity click handlers
+
+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).
+On top of that, a `$fn` function can be used to add custom behaviour.
+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.
+
+```yaml
+type: custom:plotly-graph
+entities:
+ - entity: sensor.temperature1
+ on_legend_click: |-
+ $fn () => (event_data) => {
+ event = new Event( "hass-more-info")
+ event.detail = { entityId: 'sensor.temperature1' };
+ document.querySelector('home-assistant').dispatchEvent(event);
+ return false; // disable trace toggling
+ }
+```
+
+Alternatively, clicking on points of the trace itself.
+
+```yaml
+type: custom:plotly-graph
+entities:
+ - entity: sensor.temperature1
+ on_click: |-
+ $fn () => (event_data) => {
+ ...
+ // WARNING: this doesn't work and I don't understand why. Help welcome
+ }
+```
+
+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.
+
+```yaml
+type: custom:plotly-graph
+entities:
+ - entity: sensor.temperature1
+on_dblclick: |-
+ $fn ({ hass }) => () => {
+ hass.callService('light', 'turn_on', {
+ entity_id: 'light.portique_lumiere'
+ })
+ }
+```
+
+See more in plotly's [official docs](https://plotly.com/javascript/plotlyjs-events)
+
+## Universal functions
Javascript functions allowed everywhere in the yaml. Evaluation is top to bottom and shallow to deep (depth first traversal).
diff --git a/src/filters/fft-regression.js b/src/filters/fft-regression.js
index 009ac91..9d8c0e2 100644
--- a/src/filters/fft-regression.js
+++ b/src/filters/fft-regression.js
@@ -25,7 +25,6 @@ export default class FFTRegression extends BaseRegression {
const sorted = Array.from(re.data)
.map((x, i) => [x, i])
.sort((a, b) => b[0] - a[0]);
- console.log(`sorted`, sorted);
for (let i = degree; i < sorted.length; i++) {
re.set(sorted[i][1], 0);
diff --git a/src/parse-config/defaults.ts b/src/parse-config/defaults.ts
index 607fe79..ab1c7d2 100644
--- a/src/parse-config/defaults.ts
+++ b/src/parse-config/defaults.ts
@@ -3,12 +3,15 @@ import { Config, InputConfig } from "../types";
import { parseColorScheme } from "./parse-color-scheme";
import { getEntityIndex } from "./parse-config";
import getThemedLayout, { HATheme } from "./themed-layout";
-
+const noop$fn = () => () => {};
const defaultEntityRequired = {
entity: "",
show_value: false,
internal: false,
time_offset: "0s",
+ on_legend_click: noop$fn,
+ on_legend_dblclick: noop$fn,
+ on_click: noop$fn,
};
const defaultEntityOptional = {
mode: "lines",
@@ -59,6 +62,7 @@ const defaultYamlRequired = {
yaxes: {},
},
layout: {},
+ on_dblclick: noop$fn,
};
//
diff --git a/src/plotly-graph-card.ts b/src/plotly-graph-card.ts
index e11c7f7..5b49d9f 100644
--- a/src/plotly-graph-card.ts
+++ b/src/plotly-graph-card.ts
@@ -50,6 +50,10 @@ export class PlotlyGraph extends HTMLElement {
relayoutListener?: EventEmitter;
restyleListener?: EventEmitter;
refreshTimeout?: number;
+ legendItemClick?: EventEmitter;
+ legendItemDoubleclick?: EventEmitter;
+ dataClick?: EventEmitter;
+ doubleclick?: EventEmitter;
} = {};
constructor() {
@@ -161,6 +165,18 @@ export class PlotlyGraph extends HTMLElement {
"plotly_restyle",
this.onRestyle
)!;
+ this.handles.legendItemClick = this.contentEl.on(
+ "plotly_legendclick",
+ this.onLegendItemClick
+ )!;
+ this.handles.legendItemDoubleclick = this.contentEl.on(
+ "plotly_legenddoubleclick",
+ this.onLegendItemDoubleclick
+ )!;
+ this.handles.doubleclick = this.contentEl.on(
+ "plotly_doubleclick",
+ this.onDoubleclick
+ )!;
this.resetButtonEl.addEventListener("click", this.exitBrowsingMode);
this.touchController.connect();
this.plot({ should_fetch: true });
@@ -170,6 +186,16 @@ export class PlotlyGraph extends HTMLElement {
this.handles.resizeObserver!.disconnect();
this.handles.relayoutListener!.off("plotly_relayout", this.onRelayout);
this.handles.restyleListener!.off("plotly_restyle", this.onRestyle);
+ this.handles.legendItemClick!.off(
+ "plotly_legendclick",
+ this.onLegendItemClick
+ )!;
+ this.handles.legendItemDoubleclick!.off(
+ "plotly_legenddoubleclick",
+ this.onLegendItemDoubleclick
+ )!;
+ this.handles.dataClick!.off("plotly_click", this.onDataClick)!;
+ this.handles.doubleclick!.off("plotly_doubleclick", this.onDoubleclick)!;
clearTimeout(this.handles.refreshTimeout!);
this.resetButtonEl.removeEventListener("click", this.exitBrowsingMode);
this.touchController.disconnect();
@@ -261,6 +287,27 @@ export class PlotlyGraph extends HTMLElement {
await this.plot({ should_fetch: true });
});
};
+ onLegendItemClick = ({ curveNumber, ...rest }) => {
+ return this.parsed_config.entities[curveNumber].on_legend_click({
+ curveNumber,
+ ...rest,
+ });
+ };
+ onLegendItemDoubleclick = ({ curveNumber, ...rest }) => {
+ return this.parsed_config.entities[curveNumber].on_legend_dblclick({
+ curveNumber,
+ ...rest,
+ });
+ };
+ onDataClick = ({ points, ...rest }) => {
+ return this.parsed_config.entities[points[0].curveNumber].on_click({
+ points,
+ ...rest,
+ });
+ };
+ onDoubleclick = () => {
+ return this.parsed_config.on_dblclick();
+ };
onRestyle = async () => {
// trace visibility changed, fetch missing traces
if (this.isInternalRelayout) return;
@@ -346,7 +393,6 @@ export class PlotlyGraph extends HTMLElement {
.map((e) => "" + (e || "See devtools console") + "")
.join("\n
\n");
this.parsed_config = parsed;
- console.log("fetched", this.parsed_config);
const { entities, layout, config, refresh_interval } = this.parsed_config;
clearTimeout(this.handles.refreshTimeout!);
@@ -364,6 +410,11 @@ export class PlotlyGraph extends HTMLElement {
await Plotly.react(this.contentEl, entities, layout, config);
this.contentEl.style.visibility = "";
});
+ // this.handles.dataClick?.off("plotly_click", this.onDataClick)!;
+ this.handles.dataClick = this.contentEl.on(
+ "plotly_click",
+ this.onDataClick
+ )!;
});
// The height of your card. Home Assistant uses this to automatically
// distribute all cards over the available columns.
diff --git a/src/types.ts b/src/types.ts
index 392ba7b..9fc5219 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -28,7 +28,6 @@ export type InputConfig = {
statistic?: StatisticType;
period?: StatisticPeriod | "auto" | AutoPeriodConfig;
unit_of_measurement?: string;
- lambda?: string;
internal?: boolean;
show_value?:
| boolean
@@ -38,11 +37,15 @@ export type InputConfig = {
offset?: TimeDurationStr;
extend_to_present?: boolean;
filters?: (Record | string)[];
+ on_legend_click?: Function;
+ on_legend_dblclick?: Function;
+ on_click?: Function;
} & Partial)[];
defaults?: {
entity?: Partial;
yaxes?: Partial;
};
+ on_dblclick?: Function;
layout?: Partial;
config?: Partial;
ha_theme?: boolean;
@@ -54,14 +57,6 @@ export type InputConfig = {
export type EntityConfig = EntityIdConfig & {
unit_of_measurement?: string;
- lambda?: (
- y: YValue[],
- x: Date[],
- raw_entity: ((StatisticValue | HassEntity) & {
- timestamp: number;
- value: any;
- })[]
- ) => YValue[] | { x?: Date[]; y?: YValue[] };
internal: boolean;
show_value:
| boolean
@@ -71,6 +66,9 @@ export type EntityConfig = EntityIdConfig & {
offset: number;
extend_to_present: boolean;
filters: FilterFn[];
+ on_legend_click: Function;
+ on_legend_dblclick: Function;
+ on_click: Function;
} & Partial;
export type Config = {
@@ -87,6 +85,7 @@ export type Config = {
minimal_response: boolean;
disable_pinch_to_zoom: boolean;
visible_range: [number, number];
+ on_dblclick: Function;
};
export type EntityIdStateConfig = {
entity: string;