moonraker
moonraker copied to clipboard
Feature: Generic sensor support and data logging
This feature implements a sensor component that can be used to track/log generic sensors from multiple sources. Each sensor can have properties like unit of measurement, accuracy and a display name that help frontends display the tracked measurements.
Some supported sources could be locally connected I2C, SPI or 1-wire sensors as well as remote sensor providers like MQTT or REST API driven sensor.
Example of the component in use with a ESPHome device connected through MQTT:
[sensor.py:component_init()] - Initializing sensor component
[sensor.py:initialize()] - Registered sensor 'Chamber Temperature'
[sensor.py:initialize()] - Registered sensor 'Chamber Humidity'
[sensor.py:initialize()] - Registered sensor 'Power Usage'
[sensor.py:component_init()] - All sensors have been initialized
...
[mqtt.py:__call__()] - MQTT Subscriptions Acknowledged
Topic: devvm-mjonuschat.sharks-with-lasers.net/moonraker/api/request | Granted QoS 0
Topic: hank/sensor/chamber_temperature/state | Granted QoS 0
Topic: hank/sensor/chamber_humidity/state | Granted QoS 0
Topic: hank/sensor/pzem-004t_v3_power/state | Granted QoS 0
[sensor.py:_on_state_update()] - Context: {'payload': '27.7'}
[sensor.py:_on_state_update()] - Received updated sensor value for Chamber Temperature: 27.7°C
[sensor.py:_on_state_update()] - Context: {'payload': '44.9'}
[sensor.py:_on_state_update()] - Received updated sensor value for Chamber Humidity: 44.9%
[sensor.py:_on_state_update()] - Context: {'payload': '1.60'}
[sensor.py:_on_state_update()] - Received updated sensor value for Power Usage: 1.6W
...
Querying the aggregated endpoint to retrieve sensor values:
{
"result": {
"chamber_temperature": {
"source": "hank/sensor/chamber_temperature/state",
"name": "Chamber Temperature",
"unit_of_measurement": "°C",
"accuracy_decimals": 1,
"measurements": [
0,
27.7,
27.7,
27.7,
27.7,
27.7,
27.7,
27.7,
27.7,
27.7,
27.7,
27.7,
27.7
]
},
"chamber_humidity": {
"source": "hank/sensor/chamber_humidity/state",
"name": "Chamber Humidity",
"unit_of_measurement": "%",
"accuracy_decimals": 1,
"measurements": [
0,
44.9,
44.9,
44.9,
44.9,
44.9,
44.9,
44.9,
44.9,
44.9,
44.9,
44.9,
44.9
]
},
"power_usage": {
"source": "hank/sensor/pzem-004t_v3_power/state",
"name": "Power Usage",
"unit_of_measurement": "W",
"accuracy_decimals": 2,
"measurements": [
0,
1.6,
1.6,
1.6,
1.6,
1.6,
1.6,
1.6,
1.6,
1.6,
1.6,
1.6,
1.6
]
}
}
}
Signed-off-by: Morton Jonuschat [email protected]
Thanks. I'm open to discussing generic sensor support, however the aioesphomeapi
dependency will create an issue. It depends on noiseprotocol
, which depends on cryptography
. Unfortunately the Rust bindings in cryptography
will cause a problem on SBCs that don't have prebuilt wheels available (ie: Armbian). The wheel won't build and Moonraker will fail to update. Initial support for ESPHome devices would probably need to be accomplished via MQTT. In the future when Moonraker reaches stable we could revisit this, as distribution and updates for stable releases would include prebuilt dependencies.
Separately, I think this can be accomplished though a single component. Its acceptable to break the component into separate modules if necessary, that could be accomplished by making the component a package (see the file_manager
and update_manager
components as examples). I don't believe we need a separate data store module though, it seems that each base sensor could store whatever data is necessary, then sensor.py
could expose endpoints for querying the data.
Thanks for the feedback - I didn't think about the crypto dependencies and will happily change to the proposed MQTT implementation as well as integrate the data storage into the base sensor which would get rid of the data store component. The esphome component made sense (to me) as I am currently working on also integrating it to control WLED style LEDstrips and switches/relays connected to the ESPhome device - at that point it seemed sensible to have a single connection that you configure and the downstream implementations would use that. Moving to MQTT will make this obsolete so it shouldn't be an issue and everything can be contained in the sensor component.
I've implemented the changes (storing the data in the sensor, not adding any new dependencies and providing a MQTT based implementation). I'll add some documentation over the weekend.
Thanks, the configuration documentation provided me with a more generalized idea of what you are looking to accomplish. I think this module adds value, and I have added some review comments.
With regard to the API, we probably need a couple of endpoints. One that returns current device info (including its current value), and one that returns the queue stored values for each device. I touched on what that might look like in the review comments. The info
endpoint should have an option for consumers to specify a specific device.
Additionally, most clients want changes in the value pushed to them over the websocket. In the review I mentioned having a single timer that updates the queue for each registered sensor. This timer could also push emit a sensor_update
event that is registered as a websocket notification. This event would only include sensors that detected a change, if no sensor values change then it wont be emitted. A one second timer is probably appropriate.
@Arksine : I think I've addressed most of the comments / implemented the suggested changes. Have not yet tested this fully, wanted to check in to see that this is going in the direction you were thinking. In regards to the notifications I have implemented those with the only notify when readings changed but it feels a bit counter-intuitive for sensors where you would expect to get a time-series with predictable intervals between the readings, even if say the temperature didn't change (unless it's expected that the clients "fill the gaps").
Tested and verified working with the example configuration and notification have been added
It is close, just a couple of minor issues and the "notification bundling". Additionally the APIs and websocket notification will need documentation, then it will be ready to merge.
Other than the missing sensor event name the changes to the source look good. You need to rebase your branch against master to resolve the merge conflicts with api_changes.md
, frankly you don't need to update that if you don't want. That is mostly for breaking changes. Very soon I plan to keep a single changelog.
The websocket notification needs to be documented in web_api.md
. Since you basically have it documented in api_changes
you could move it with a few tweaks.
Requested changes have been addressed and changes have been rebased. Let me know if you want me to squash the changes before merging.
Thanks. I found some issues with the docs, and apparently I missed a problem with the measurements
request in my last review. I also proposed refactoring the name of /server/sensors/sensor
to /server/sensors/info
, as the /server/sensors/sensor?sensor=
is a bit extreme on the use of the term "sensor".
Sorry for the force-push, forgot to add the Signed-off comment to a bunch of the commit messages.
Tested the endpoint changes and the bugfix:
pi@moonraker-dev:~ $ curl -s "http://localhost:7125/server/sensors/measurements" | jq
{
"result": {
"powermeter": {
"power": [
0,
0,
0
],
"voltage": [
115.7,
115.7,
115.7
],
"current": [
0,
0,
0
],
"energy": [
0,
0,
0
]
}
}
}
pi@moonraker-dev:~ $ curl -s "http://localhost:7125/server/sensors/measurements?sensor=powermeter" | jq
{
"result": {
"powermeter": {
"power": [
0,
0
],
"voltage": [
115.4,
115.4
],
"current": [
0,
0
],
"energy": [
0,
0
]
}
}
}
pi@moonraker-dev:~ $ curl -s "http://localhost:7125/server/sensors/info?sensor=powermeter"|jq
{
"result": {
"id": "powermeter",
"friendly_name": "powermeter",
"type": "mqtt",
"values": {
"power": 0,
"voltage": 115.4,
"current": 0,
"energy": 0
}
}
}
Thanks.