Refactor ValueRenderer action and meta info layout
This commit is contained in:
115
Readme.tpl.md
115
Readme.tpl.md
@@ -1,115 +0,0 @@
|
|||||||
# [MQTT Explorer](https://thomasnordquist.github.io/MQTT-Explorer/)
|
|
||||||
|
|
||||||
[](https://travis-ci.org/thomasnordquist/MQTT-Explorer/releases)
|
|
||||||
[](https://travis-ci.org/thomasnordquist/MQTT-Explorer/releases)
|
|
||||||
[](https://travis-ci.org/thomasnordquist/MQTT-Explorer)
|
|
||||||
[](https://ci.appveyor.com/project/thomasnordquist/mqtt-explorer/branch/master)
|
|
||||||
[](https://app.codacy.com/app/thomasnordquist/MQTT-Explorer?utm_source=github.com&utm_medium=referral&utm_content=thomasnordquist/MQTT-Explorer&utm_campaign=Badge_Grade_Dashboard)
|
|
||||||
|
|
||||||
| | | |
|
|
||||||
|:---:|:---:|:---:|
|
|
||||||
| [](https://user-images.githubusercontent.com/7721625/53954364-52551f00-40d6-11e9-93cf-d5a9601897ea.png) | [](https://user-images.githubusercontent.com/7721625/53954365-52551f00-40d6-11e9-823f-afd66f19ed01.png) | [](https://user-images.githubusercontent.com/7721625/53954366-52551f00-40d6-11e9-9738-74db830d03ac.png) |
|
|
||||||
|
|
||||||
## Version {{ version }}
|
|
||||||
|
|
||||||
MQTT Explorer is a comprehensive MQTT client that provides a structured overview of your MQTT topics and makes working with devices/services on your broker dead-simple.
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
- Visualize topics and topic activity
|
|
||||||
- Delete retained topics
|
|
||||||
- Search/filter topics
|
|
||||||
- Delete topics recursively
|
|
||||||
- Publish topics
|
|
||||||
- Plot numeric topics
|
|
||||||
- Retain a history of each topic
|
|
||||||
- ...
|
|
||||||
|
|
||||||
The hierarchical view makes this tool so easy to use and differentiates the **MQTT Explorer** from other great MQTT clients like [MQTTLens](https://chrome.google.com/webstore/detail/mqttlens/hemojaaeigabkbcookmlgmdigohjobjm), [MQTTBox](http://workswithweb.com/mqttbox.html) and [MQTT.fx](https://mqttfx.jensd.de/).
|
|
||||||
This MQTT Client strives to be a MQTT swiss-army-knife, the perfect tool to integrate new services and IoT devices on your network.
|
|
||||||
|
|
||||||
## Download
|
|
||||||
|
|
||||||
Developing this tool takes a lot of effort, sweat and time, please consider rating the App on the Windows or Mac app store <img src="./res/star.svg" width="16" /><img src="./res/star.svg" width="16" /><img src="./res/star.svg" width="16" /><img src="./res/star.svg" width="16" /><img src="./res/star.svg" width="16" />.
|
|
||||||
If you feel like a feature is missing or you found a bug, please leave me a [comment / issue](https://github.com/thomasnordquist/MQTT-Explorer/issues) and I'll see what I can do.
|
|
||||||
|
|
||||||
| Platform | | Downloads |
|
|
||||||
|:----------:|:-------------:|:------:|
|
|
||||||
|  | **Windows** | <a href="https://www.microsoft.com/store/apps/9PP8SFM082WD?ocid=badge"><img src="https://assets.windowsphone.com/85864462-9c82-451e-9355-a3d5f874397a/English_get-it-from-MS_InvariantCulture_Default.png" width="165" /></a><br />**{{windowsTargets}}** |
|
|
||||||
|  | **Mac** | <a href="https://geo.itunes.apple.com/us/app/mqtt-explorer/id1455214828?mt=12&app=apps"><img src="https://linkmaker.itunes.apple.com/en-us/badge-lrg.svg?releaseDate=2019-03-07T00:00:00Z&kind=desktopapp&bubble=macos_apps" width="165" height="40"/></a><br />**{{macTargets}}** |
|
|
||||||
|  | **Ubuntu**<br />*also for debian, mint, neon, fedora, etc...* | <a href="https://snapcraft.io/mqtt-explorer" title="Get it from the Snap Store"><img src="https://snapcraft.io/static/images/badges/en/snap-store-black.svg" width="165" /></a><br />`snap install mqtt-explorer`<br />[Ubuntu Store](snap://mqtt-explorer) |
|
|
||||||
|  | **Linux**<br />*almost every linux* | **{{linuxTargets}}**<br />*Run AppImage:<br />Make it executable and double-click it.* |
|
|
||||||
|
|
||||||
[More Downloads](https://github.com/thomasnordquist/MQTT-Explorer/releases)
|
|
||||||
|
|
||||||
## Video
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## Performance
|
|
||||||
|
|
||||||
This MQTT Client is optimized to handle thousands of topics and at hundreds of thousands messages per minute.
|
|
||||||
|
|
||||||
Custom subscriptions can limit the amount of messages **MQTT Explorer** needs to process, subscriptions can be managed in the advanced connection settings.
|
|
||||||
In very large productive environments brokers may handle an extreme load of topics, subscribing with a wildcard topic is in this scenario not advised.
|
|
||||||
|
|
||||||
## IoT Applications
|
|
||||||
|
|
||||||
List of useful IoT applications using MQTT to integrate devices / services
|
|
||||||
|
|
||||||
- [Home Assistant](https://www.home-assistant.io/) - Open source home automation gateway
|
|
||||||
- [OpenHAB](https://www.openhab.org/) - Smart Home Gateway
|
|
||||||
- [Node-RED](https://nodered.org/) - Flow-based programming for the Internet of Things
|
|
||||||
- [zigbee2mqtt](https://github.com/Koenkk/zigbee2mqtt) - A ZigBee to MQTT bridge
|
|
||||||
- [Tasmota](https://github.com/arendst/Sonoff-Tasmota) - ESP8266 firmware with MQTT support
|
|
||||||
|
|
||||||
## Develop
|
|
||||||
|
|
||||||
PRs and issues are welcome.
|
|
||||||
Install with `npm run install`, build with `npm run build`
|
|
||||||
|
|
||||||
Start with `npm run start`
|
|
||||||
|
|
||||||
The `app` directory contains all the rendering logic, the `backend` directory currently contains the models, tests, connection management, `src` contains all the electron bindings. [mqttjs](https://github.com/mqttjs/MQTT.js) is used to facilitate communication to MQTT brokers.
|
|
||||||
|
|
||||||
## Automated Tests
|
|
||||||
|
|
||||||
To achieve a reliable product automated tests run regularly on travis.
|
|
||||||
|
|
||||||
- Data model
|
|
||||||
- MQTT integration
|
|
||||||
- UI-Tests (The demo is a recorded ui test)
|
|
||||||
|
|
||||||
A [mosquitto](https://mosquitto.org/) MQTT broker is required to run the ui-tests.
|
|
||||||
|
|
||||||
## Telemetry
|
|
||||||
|
|
||||||
No personal data is processed, sent or stored.
|
|
||||||
|
|
||||||
The app sends telemetry and error reports, this enables me to quickly react on bugs/errors and understand what's going on. Responding quickly to errors is one key element in producing a reliable software product.
|
|
||||||
|
|
||||||
It basically sends: app version, processor architecture, operating system, used memory, user interactions and error stacks.
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>Example telemetry
|
|
||||||
</summary>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
{ system: { arch: 'x64', platform: 'darwin' },
|
|
||||||
appVersion: '0.0.7',
|
|
||||||
events: { HELLO_EVENT: [ 1547714886134 ] },
|
|
||||||
now: 1547714886135,
|
|
||||||
transactionId: '1767d251-f492-4f2c-aa62-88add3acc26b' }
|
|
||||||
{ errors:
|
|
||||||
[ { time: 1547714887921,
|
|
||||||
message: 'He\'s dead Jim!',
|
|
||||||
stack:
|
|
||||||
'Error: He\'s dead Jim!\n at ./src/tracking.ts.exports.default (./mqtt-explorer/app/build/bundle.js:142765:11)\n at new Promise (<anonymous>)\n at Object../src/tracking.ts (./mqtt-explorer/app/build/bundle.js:142764:1)\n at __webpack_require__ (./mqtt-explorer/app/build/bundle.js:20:30)\n at Object../src/index.tsx (./mqtt-explorer/app/build/bundle.js:142618:1)\n at __webpack_require__ (./mqtt-explorer/app/build/bundle.js:20:30)\n at ../backend/node_modules/charenc/charenc.js.charenc.utf8.stringToBytes (./mqtt-explorer/app/build/bundle.js:84:18)\n at ./mqtt-explorer/app/build/bundle.js:87:10' } ],
|
|
||||||
now: 1547714887921,
|
|
||||||
transactionId: '53bf9aac-e695-40cc-9a81-b1cf3398843d' }
|
|
||||||
```
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
Even though the data is purely technical, an option to disable telemetry is planned. [#52](https://github.com/thomasnordquist/MQTT-Explorer/issues/52)
|
|
||||||
|
|
||||||
@@ -62,7 +62,7 @@ class ValuePanel extends React.Component<Props, State> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private messageMetaInfo() {
|
private renderViewOptions() {
|
||||||
if (!this.props.node || !this.props.node.message || !this.props.node.mqttMessage) {
|
if (!this.props.node || !this.props.node.message || !this.props.node.mqttMessage) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@@ -83,29 +83,41 @@ class ValuePanel extends React.Component<Props, State> {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ width: '100%', display: 'flex', paddingLeft: '8px' }}>
|
<div style={{ width: '100%', display: 'flex', paddingLeft: '8px' }}>
|
||||||
<div style={{ flex: 6 }}><Typography>QoS: {this.props.node.mqttMessage.qos}</Typography></div>
|
<span style={{ marginTop: '-8px', flexGrow: 1 }}>{this.renderActionButtons()}</span>
|
||||||
<span style={{ marginTop: '-8px' }}>{this.renderActionButtons()}</span>
|
<div style={{ flex: 6, textAlign: 'right' }}>
|
||||||
<div style={{ flex: 8, textAlign: 'center' }}>
|
|
||||||
{this.props.node.mqttMessage.retain ? retainedButton : null}
|
{this.props.node.mqttMessage.retain ? retainedButton : null}
|
||||||
</div>
|
</div>
|
||||||
<div style={{ flex: 8, textAlign: 'right' }}><Typography><i><DateFormatter date={this.props.node.message.received} /></i></Typography></div>
|
{this.messageMetaInfo()}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private messageMetaInfo() {
|
||||||
|
if (!this.props.node || !this.props.node.message || !this.props.node.mqttMessage) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span style={{ width: '100%', paddingLeft: '8px', flex: 6, marginTop: '-14px' }}>
|
||||||
|
<Typography style={{ textAlign: 'right' }}>QoS: {this.props.node.mqttMessage.qos}</Typography>
|
||||||
|
<Typography style={{ textAlign: 'right' }}><i><DateFormatter date={this.props.node.message.received} /></i></Typography>
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private renderActionButtons() {
|
private renderActionButtons() {
|
||||||
const handleValue = (_e: React.MouseEvent, value: any) => {
|
const handleValue = (mouseEvent: React.MouseEvent, value: any) => {
|
||||||
this.props.settingsActions.setValueDisplayMode(value)
|
this.props.settingsActions.setValueDisplayMode(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ToggleButtonGroup id="valueRendererDisplayMode" value={this.props.valueRendererDisplayMode} exclusive={true} onChange={handleValue}>
|
<ToggleButtonGroup id="valueRendererDisplayMode" value={this.props.valueRendererDisplayMode} exclusive={true} onChange={handleValue}>
|
||||||
<ToggleButton value="diff" id="valueRendererDisplayMode-diff">
|
<ToggleButton className={this.props.classes.toggleButton} value="diff" id="valueRendererDisplayMode-diff">
|
||||||
<Tooltip title="Show difference between the current and the last message">
|
<Tooltip title="Show difference between the current and the last message">
|
||||||
<Code />
|
<Code />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</ToggleButton>
|
</ToggleButton>
|
||||||
<ToggleButton value="raw" id="valueRendererDisplayMode-raw">
|
<ToggleButton className={this.props.classes.toggleButton} value="raw" id="valueRendererDisplayMode-raw">
|
||||||
<Tooltip title="Raw value">
|
<Tooltip title="Raw value">
|
||||||
<Reorder />
|
<Reorder />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
@@ -137,19 +149,19 @@ class ValuePanel extends React.Component<Props, State> {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<ExpansionPanel key="value" defaultExpanded={true}>
|
<ExpansionPanel key="value" defaultExpanded={true}>
|
||||||
<ExpansionPanelSummary expandIcon={<ExpandMore />} style={summaryStyle}>
|
<ExpansionPanelSummary expandIcon={<ExpandMore />} style={summaryStyle}>
|
||||||
<Typography className={classes.heading}>Value {copyValue}</Typography>
|
<Typography className={classes.heading}>Value {copyValue}</Typography>
|
||||||
</ExpansionPanelSummary>
|
</ExpansionPanelSummary>
|
||||||
<ExpansionPanelDetails style={detailsStyle}>
|
<ExpansionPanelDetails style={detailsStyle}>
|
||||||
{this.messageMetaInfo()}
|
{this.renderViewOptions()}
|
||||||
<div>
|
<div>
|
||||||
<React.Suspense fallback={<div>Loading...</div>}>
|
<React.Suspense fallback={<div>Loading...</div>}>
|
||||||
{this.renderValue()}
|
{this.renderValue()}
|
||||||
</React.Suspense>
|
</React.Suspense>
|
||||||
</div>
|
</div>
|
||||||
<div><MessageHistory onSelect={this.handleMessageHistorySelect} selected={this.state.compareMessage} node={this.props.node} /></div>
|
<div><MessageHistory onSelect={this.handleMessageHistorySelect} selected={this.state.compareMessage} node={this.props.node} /></div>
|
||||||
</ExpansionPanelDetails>
|
</ExpansionPanelDetails>
|
||||||
</ExpansionPanel>
|
</ExpansionPanel>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -174,7 +186,10 @@ const styles: StyleRulesCallback<string> = (theme: Theme) => {
|
|||||||
fontSize: theme.typography.pxToRem(15),
|
fontSize: theme.typography.pxToRem(15),
|
||||||
fontWeight: theme.typography.fontWeightRegular,
|
fontWeight: theme.typography.fontWeightRegular,
|
||||||
},
|
},
|
||||||
|
toggleButton: {
|
||||||
|
height: '36px',
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default withStyles(styles)(connect(mapStateToProps, mapDispatchToProps)(ValuePanel))
|
export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(ValuePanel))
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ class ValueRenderer extends React.Component<Props, State> {
|
|||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
return (
|
return (
|
||||||
<div style={{ padding: '8px 0px 8px 8px' }}>
|
<div style={{ padding: '0px 0px 8px 8px' }}>
|
||||||
<ReactResizeDetector handleWidth={true} onResize={this.updateWidth} />
|
<ReactResizeDetector handleWidth={true} onResize={this.updateWidth} />
|
||||||
{this.renderValue()}
|
{this.renderValue()}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user