= {
next: (event: LiveChannelEvent) => {
if (isLiveChannelStatusEvent(event)) {
this.setState({ status: event, changed: Date.now() });
} else if (isLiveChannelMessageEvent(event)) {
this.setState({ message: event.message, changed: Date.now() });
} else {
console.log('ignore', event);
}
},
};
unsubscribe = () => {
if (this.subscription) {
this.subscription.unsubscribe();
this.subscription = undefined;
}
};
async loadChannel() {
const addr = this.props.options?.channel;
if (!isValidLiveChannelAddress(addr)) {
console.log('INVALID', addr);
this.unsubscribe();
this.setState({
addr: undefined,
});
return;
}
if (isEqual(addr, this.state.addr)) {
console.log('Same channel', this.state.addr);
return;
}
const live = getGrafanaLiveSrv();
if (!live) {
console.log('INVALID', addr);
this.unsubscribe();
this.setState({
addr: undefined,
});
return;
}
this.unsubscribe();
console.log('LOAD', addr);
// Subscribe to new events
try {
this.subscription = live.getStream(addr).subscribe(this.streamObserver);
this.setState({ addr, error: undefined });
} catch (err) {
this.setState({ addr: undefined, error: err });
}
}
renderNotEnabled() {
const preformatted = `[feature_toggles]
enable = live`;
return (
Grafana live requires a feature flag to run
custom.ini:
{preformatted}
);
}
onSaveJSON = (text: string) => {
const { options, onOptionsChange } = this.props;
try {
const json = JSON.parse(text);
onOptionsChange({ ...options, json });
} catch (err) {
console.log('Error reading JSON', err);
}
};
onPublishClicked = async () => {
const { addr } = this.state;
if (!addr) {
console.log('invalid address');
return;
}
const data = this.props.options?.json;
if (!data) {
console.log('nothing to publish');
return;
}
const rsp = await getGrafanaLiveSrv().publish(addr, data);
console.log('onPublishClicked (response from publish)', rsp);
};
renderMessage(height: number) {
const { options } = this.props;
const { message } = this.state;
if (!message) {
return (
Waiting for data:
{options.channel?.scope}/{options.channel?.namespace}/{options.channel?.path}
);
}
if (options.message === MessageDisplayMode.JSON) {
return ;
}
if (options.message === MessageDisplayMode.Auto) {
if (message instanceof StreamingDataFrame) {
const data: PanelData = {
series: applyFieldOverrides({
data: [message],
theme: config.theme2,
replaceVariables: (v: string) => v,
fieldConfig: {
defaults: {},
overrides: [],
},
}),
state: LoadingState.Streaming,
} as PanelData;
const props: PanelProps = {
...this.props,
options: { frameIndex: 0, showHeader: true },
};
return ;
}
}
return {JSON.stringify(message)}
;
}
renderPublish(height: number) {
const { options } = this.props;
return (
<>
>
);
}
renderStatus() {
const { status } = this.state;
if (status?.state === LiveChannelConnectionState.Connected) {
return; // nothing
}
let statusClass = '';
if (status) {
statusClass = this.styles.status[status.state];
}
return {status?.state}
;
}
renderBody() {
const { status } = this.state;
const { options, height } = this.props;
if (options.publish) {
// Only the publish form
if (options.message === MessageDisplayMode.None) {
return {this.renderPublish(height)}
;
}
// Both message and publish
const halfHeight = height / 2;
return (
{this.renderMessage(halfHeight)}
{this.renderPublish(halfHeight)}
);
}
if (options.message === MessageDisplayMode.None) {
return {JSON.stringify(status)}
;
}
// Only message
return (
{this.renderMessage(height)}
);
}
render() {
if (!this.isValid) {
return this.renderNotEnabled();
}
const { addr, error } = this.state;
if (!addr) {
return (
Use the panel editor to pick a channel
);
}
if (error) {
return (
ERROR
{JSON.stringify(error)}
);
}
return (
<>
{this.renderStatus()}
{this.renderBody()}
>
);
}
}
const getStyles = stylesFactory((theme: GrafanaTheme2) => ({
statusWrap: css`
margin: auto;
position: absolute;
top: 0;
right: 0;
background: ${theme.components.panel.background};
padding: 10px;
z-index: ${theme.zIndex.modal};
`,
status: {
[LiveChannelConnectionState.Pending]: css`
border: 1px solid ${theme.v1.palette.orange};
`,
[LiveChannelConnectionState.Connected]: css`
border: 1px solid ${theme.colors.success.main};
`,
[LiveChannelConnectionState.Connecting]: css`
border: 1px solid ${theme.v1.palette.brandWarning};
`,
[LiveChannelConnectionState.Disconnected]: css`
border: 1px solid ${theme.colors.warning.main};
`,
[LiveChannelConnectionState.Shutdown]: css`
border: 1px solid ${theme.colors.error.main};
`,
[LiveChannelConnectionState.Invalid]: css`
border: 1px solid red;
`,
},
}));