sensor: add support to track generic single value sensors
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. Signed-off-by: Morton Jonuschat <mjonuschat+moonraker@gmail.com>
This commit is contained in:
parent
0a811b9e44
commit
b2ba52ce3d
@ -1,6 +1,20 @@
|
|||||||
##
|
##
|
||||||
This document keeps a record of all changes to Moonraker's web APIs.
|
This document keeps a record of all changes to Moonraker's web APIs.
|
||||||
|
|
||||||
|
### February 20th 2023
|
||||||
|
- The following new endpoints are available when at least one `[sensor]`
|
||||||
|
section has been configured:
|
||||||
|
- `GET /server/sensors/list`
|
||||||
|
- `GET /server/sensors/sensor`
|
||||||
|
- `GET /server/sensors/measurements`
|
||||||
|
|
||||||
|
See [web_api.md](web_api.md) for details on these new endpoints.
|
||||||
|
- A `sensors:sensor_update` notification has been added. When at least one
|
||||||
|
monitored sensor is reporting a changed value Moonraker will broadcast this
|
||||||
|
notification.
|
||||||
|
|
||||||
|
See [web_api.md](web_api.md) for details on this new notification.
|
||||||
|
|
||||||
### February 17 2023
|
### February 17 2023
|
||||||
- Moonraker API Version 1.2.1
|
- Moonraker API Version 1.2.1
|
||||||
- An error in the return value for some file manager endpoints has
|
- An error in the return value for some file manager endpoints has
|
||||||
|
@ -2187,6 +2187,93 @@ ambient_sensor:
|
|||||||
More on how your data is used in the SimplyPrint privacy policy here;
|
More on how your data is used in the SimplyPrint privacy policy here;
|
||||||
[https://simplyprint.io/legal/privacy](https://simplyprint.io/legal/privacy)
|
[https://simplyprint.io/legal/privacy](https://simplyprint.io/legal/privacy)
|
||||||
|
|
||||||
|
### `[sensor]`
|
||||||
|
|
||||||
|
Enables data collection from additional sensor sources. Multiple "sensor"
|
||||||
|
sources may be configured, each with their own section, ie: `[sensor current]`,
|
||||||
|
`[sensor voltage]`.
|
||||||
|
|
||||||
|
#### Options common to all sensor devices
|
||||||
|
|
||||||
|
The following configuration options are available for all sensor types:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
# moonraker.conf
|
||||||
|
|
||||||
|
[sensor my_sensor]
|
||||||
|
type:
|
||||||
|
# The type of device. Supported types: mqtt
|
||||||
|
# This parameter must be provided.
|
||||||
|
name:
|
||||||
|
# The friendly display name of the sensor.
|
||||||
|
# The default is the sensor source name.
|
||||||
|
```
|
||||||
|
|
||||||
|
#### MQTT Sensor Configuration
|
||||||
|
|
||||||
|
The following options are available for `mqtt` sensor types:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
# moonraker.conf
|
||||||
|
|
||||||
|
qos:
|
||||||
|
# The MQTT QOS level to use when publishing and subscribing to topics.
|
||||||
|
# The default is to use the setting supplied in the [mqtt] section.
|
||||||
|
state_topic:
|
||||||
|
# The mqtt topic to subscribe to for sensor state updates. This parameter
|
||||||
|
# must be provided.
|
||||||
|
state_response_template:
|
||||||
|
# A template used to parse the payload received with the state topic. A
|
||||||
|
# "payload" variable is provided the template's context. This template must
|
||||||
|
# call the provided set_result() method to pass sensor values to Moonraker.
|
||||||
|
# `set_result()` expects two parameters, the name of the measurement (as
|
||||||
|
# string) and the value of the measurement (either integer or float number).
|
||||||
|
#
|
||||||
|
# This allows for sensor that can return multiple readings (e.g. temperature/
|
||||||
|
# humidity sensors or powermeters).
|
||||||
|
# For example:
|
||||||
|
# {% set notification = payload|fromjson %}
|
||||||
|
# {set_result("temperature", notification["temperature"]|float)}
|
||||||
|
# {set_result("humidity", notification["humidity"]|float)}
|
||||||
|
# {set_result("pressure", notification["pressure"]|float)}
|
||||||
|
#
|
||||||
|
# The above example assumes a json response with multiple fields in a struct
|
||||||
|
# is received. Individual measurements are extracted from that struct, coerced
|
||||||
|
# to a numeric format and passed to Moonraker. The default is the payload.
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! Note
|
||||||
|
Moonraker's MQTT client must be properly configured to add a MQTT sensor.
|
||||||
|
See the [mqtt](#mqtt) section for details.
|
||||||
|
|
||||||
|
!!! Tip
|
||||||
|
MQTT is the most robust way of collecting sensor data from networked
|
||||||
|
devices through Moonraker. A well implemented MQTT sensor will publish all
|
||||||
|
changes in state to the `state_topic`. Moonraker receives these changes,
|
||||||
|
updates its internal state, and notifies connected clients.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
# moonraker.conf
|
||||||
|
|
||||||
|
# Example configuration for a Shelly Pro 1PM (Gen2) switch with
|
||||||
|
# integrated power meter running the Shelly firmware over MQTT.
|
||||||
|
[sensor mqtt_powermeter]
|
||||||
|
type: mqtt
|
||||||
|
name: Powermeter
|
||||||
|
# Use a different display name
|
||||||
|
state_topic: shellypro1pm-8cb113caba09/status/switch:0
|
||||||
|
# The response is a JSON object with a multiple fields that we convert to
|
||||||
|
# float values before passing them to Moonraker.
|
||||||
|
state_response_template:
|
||||||
|
{% set notification = payload|fromjson %}
|
||||||
|
{set_result("power", notification["apower"]|float)}
|
||||||
|
{set_result("voltage", notification["voltage"]|float)}
|
||||||
|
{set_result("current", notification["current"]|float)}
|
||||||
|
{set_result("energy", notification["aenergy"]["by_minute"][0]|float * 0.000001)}
|
||||||
|
```
|
||||||
|
|
||||||
## Include directives
|
## Include directives
|
||||||
|
|
||||||
It is possible to include configuration from other files via include
|
It is possible to include configuration from other files via include
|
||||||
|
172
docs/web_api.md
172
docs/web_api.md
@ -4915,6 +4915,154 @@ State of the strip.
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Sensor APIs
|
||||||
|
The APIs below are available when the `[sensor]` component has been configured.
|
||||||
|
|
||||||
|
#### Get Sensor List
|
||||||
|
HTTP request:
|
||||||
|
```http
|
||||||
|
GET /server/sensors/list
|
||||||
|
```
|
||||||
|
JSON-RPC request:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"method":"server.sensors.list",
|
||||||
|
"id": 5646
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
An array of objects containing info for each configured sensor.
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"sensors": {
|
||||||
|
"sensor1": {
|
||||||
|
"id": "sensor1",
|
||||||
|
"friendly_name": "Sensor 1",
|
||||||
|
"type": "mqtt",
|
||||||
|
"values": {
|
||||||
|
"value1": 0,
|
||||||
|
"value2": 119.8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Get Sensor Information
|
||||||
|
Returns the status for a single configured sensor.
|
||||||
|
|
||||||
|
HTTP request:
|
||||||
|
```http
|
||||||
|
GET /server/sensors/info?sensor=sensor1
|
||||||
|
```
|
||||||
|
JSON-RPC request:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"method": "/server/sensors/info?sensor=sensor1",
|
||||||
|
"params": {
|
||||||
|
"sensor": "sensor1"
|
||||||
|
},
|
||||||
|
"id": 4564
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
An object containing sensor information for the requested sensor:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "sensor1",
|
||||||
|
"friendly_name": "Sensor 1",
|
||||||
|
"type": "mqtt",
|
||||||
|
"values": {
|
||||||
|
"value1": 0.0,
|
||||||
|
"value2": 120.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Get Sensor Measurements
|
||||||
|
Returns all recorded measurements for a configured sensor.
|
||||||
|
|
||||||
|
HTTP request:
|
||||||
|
```http
|
||||||
|
GET /server/sensors/measurements?sensor=sensor1
|
||||||
|
```
|
||||||
|
JSON-RPC request:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"method": "server.sensors.measurements",
|
||||||
|
"params": {
|
||||||
|
"sensor": "sensor1"
|
||||||
|
},
|
||||||
|
"id": 4564
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
An object containing all recorded measurements for the requested sensor:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"sensor1": {
|
||||||
|
"value1": [
|
||||||
|
3.1,
|
||||||
|
3.2,
|
||||||
|
3.0
|
||||||
|
],
|
||||||
|
"value2": [
|
||||||
|
120.0,
|
||||||
|
120.0,
|
||||||
|
119.9
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Get Batch Sensor Measurements
|
||||||
|
Returns recorded measurements for all sensors.
|
||||||
|
|
||||||
|
HTTP request:
|
||||||
|
```http
|
||||||
|
GET /server/sensors/measurements
|
||||||
|
```
|
||||||
|
JSON-RPC request:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"method": "server.sensors.measurements",
|
||||||
|
"id": 4564
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
An object containing all measurements for every configured sensor:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"sensor1": {
|
||||||
|
"value1": [
|
||||||
|
3.1,
|
||||||
|
3.2,
|
||||||
|
3.0
|
||||||
|
],
|
||||||
|
"value2": [
|
||||||
|
120.0,
|
||||||
|
120.0,
|
||||||
|
119.9
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"sensor2": {
|
||||||
|
"value_a": [
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
0
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### OctoPrint API emulation
|
### OctoPrint API emulation
|
||||||
Partial support of OctoPrint API is implemented with the purpose of
|
Partial support of OctoPrint API is implemented with the purpose of
|
||||||
allowing uploading of sliced prints to a moonraker instance.
|
allowing uploading of sliced prints to a moonraker instance.
|
||||||
@ -6290,6 +6438,30 @@ disconnects clients will receive a `disconnected` event with the data field
|
|||||||
omitted. All other events are determined by the agent, where each event may
|
omitted. All other events are determined by the agent, where each event may
|
||||||
or may not include optional `data`.
|
or may not include optional `data`.
|
||||||
|
|
||||||
|
#### Sensor Events
|
||||||
|
|
||||||
|
Moonraker will emit a `sensors:sensor_update` notification when a measurement
|
||||||
|
from at least one monitored sensor changes.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"method": "sensors:sensor_update",
|
||||||
|
"params": [
|
||||||
|
{
|
||||||
|
"sensor1": {
|
||||||
|
"humidity": 28.9,
|
||||||
|
"temperature": 22.4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
When a sensor reading changes, all connections will receive a
|
||||||
|
`sensors:sensor_update` event where the params contains a data struct
|
||||||
|
with the sensor id as the key and the sensors letest measurements as value
|
||||||
|
struct.
|
||||||
|
|
||||||
### Appendix
|
### Appendix
|
||||||
|
|
||||||
#### Websocket setup
|
#### Websocket setup
|
||||||
|
309
moonraker/components/sensor.py
Normal file
309
moonraker/components/sensor.py
Normal file
@ -0,0 +1,309 @@
|
|||||||
|
# Generic sensor support
|
||||||
|
#
|
||||||
|
# Copyright (C) 2022 Morton Jonuschat <mjonuschat+moonraker@gmail.com>
|
||||||
|
#
|
||||||
|
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||||
|
|
||||||
|
# Component to read additional generic sensor data and make it
|
||||||
|
# available to clients
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from collections import defaultdict, deque
|
||||||
|
from dataclasses import dataclass, replace
|
||||||
|
from functools import partial
|
||||||
|
|
||||||
|
# Annotation imports
|
||||||
|
from typing import (
|
||||||
|
Any,
|
||||||
|
DefaultDict,
|
||||||
|
Deque,
|
||||||
|
Dict,
|
||||||
|
List,
|
||||||
|
Optional,
|
||||||
|
Type,
|
||||||
|
TYPE_CHECKING,
|
||||||
|
Union,
|
||||||
|
)
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from confighelper import ConfigHelper
|
||||||
|
from websockets import WebRequest
|
||||||
|
|
||||||
|
SENSOR_UPDATE_TIME = 1.0
|
||||||
|
SENSOR_EVENT_NAME = "sensors:sensor_update"
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class SensorConfiguration:
|
||||||
|
id: str
|
||||||
|
name: str
|
||||||
|
type: str
|
||||||
|
source: str = ""
|
||||||
|
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from confighelper import ConfigHelper
|
||||||
|
|
||||||
|
from .mqtt import MQTTClient
|
||||||
|
|
||||||
|
|
||||||
|
def _set_result(
|
||||||
|
name: str, value: Union[int, float], store: Dict[str, Union[int, float]]
|
||||||
|
) -> None:
|
||||||
|
if not isinstance(value, (int, float)):
|
||||||
|
store[name] = float(value)
|
||||||
|
else:
|
||||||
|
store[name] = value
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class Sensor:
|
||||||
|
config: SensorConfiguration
|
||||||
|
values: Dict[str, Deque[Union[int, float]]]
|
||||||
|
|
||||||
|
|
||||||
|
class BaseSensor:
|
||||||
|
def __init__(self, name: str, cfg: ConfigHelper, store_size: int = 1200) -> None:
|
||||||
|
self.server = cfg.get_server()
|
||||||
|
self.error_state: Optional[str] = None
|
||||||
|
|
||||||
|
self.config = SensorConfiguration(
|
||||||
|
id=name,
|
||||||
|
type=cfg.get("type"),
|
||||||
|
name=cfg.get("name", name),
|
||||||
|
)
|
||||||
|
self.last_measurements: Dict[str, Union[int, float]] = {}
|
||||||
|
self.last_value: Dict[str, Union[int, float]] = {}
|
||||||
|
self.values: DefaultDict[str, Deque[Union[int, float]]] = defaultdict(
|
||||||
|
lambda: deque(maxlen=store_size)
|
||||||
|
)
|
||||||
|
|
||||||
|
def _update_sensor_value(self, eventtime: float) -> None:
|
||||||
|
"""
|
||||||
|
Append the last updated value to the store.
|
||||||
|
"""
|
||||||
|
for key, value in self.last_measurements.items():
|
||||||
|
self.values[key].append(value)
|
||||||
|
|
||||||
|
# Copy the last measurements data
|
||||||
|
self.last_value = {**self.last_measurements}
|
||||||
|
|
||||||
|
async def initialize(self) -> bool:
|
||||||
|
"""
|
||||||
|
Sensor initialization executed on Moonraker startup.
|
||||||
|
"""
|
||||||
|
logging.info("Registered sensor '%s'", self.config.name)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def get_sensor_info(self) -> Dict[str, Any]:
|
||||||
|
return {
|
||||||
|
"id": self.config.id,
|
||||||
|
"friendly_name": self.config.name,
|
||||||
|
"type": self.config.type,
|
||||||
|
"values": self.last_measurements,
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_sensor_measurements(self) -> Dict[str, List[Union[int, float]]]:
|
||||||
|
return {key: list(values) for key, values in self.values.items()}
|
||||||
|
|
||||||
|
def get_name(self) -> str:
|
||||||
|
return self.config.name
|
||||||
|
|
||||||
|
def close(self) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class MQTTSensor(BaseSensor):
|
||||||
|
def __init__(self, name: str, cfg: ConfigHelper, store_size: int = 1200):
|
||||||
|
super().__init__(name=name, cfg=cfg)
|
||||||
|
self.mqtt: MQTTClient = self.server.load_component(cfg, "mqtt")
|
||||||
|
|
||||||
|
self.state_topic: str = cfg.get("state_topic")
|
||||||
|
self.state_response = cfg.load_template("state_response_template", "{payload}")
|
||||||
|
self.config = replace(self.config, source=self.state_topic)
|
||||||
|
self.qos: Optional[int] = cfg.getint("qos", None, minval=0, maxval=2)
|
||||||
|
|
||||||
|
self.server.register_event_handler(
|
||||||
|
"mqtt:disconnected", self._on_mqtt_disconnected
|
||||||
|
)
|
||||||
|
|
||||||
|
def _on_state_update(self, payload: bytes) -> None:
|
||||||
|
measurements: Dict[str, Union[int, float]] = {}
|
||||||
|
context = {
|
||||||
|
"payload": payload.decode(),
|
||||||
|
"set_result": partial(_set_result, store=measurements),
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.state_response.render(context)
|
||||||
|
except Exception as e:
|
||||||
|
logging.error("Error updating sensor results: %s", e)
|
||||||
|
self.error_state = str(e)
|
||||||
|
else:
|
||||||
|
self.error_state = None
|
||||||
|
self.last_measurements = measurements
|
||||||
|
logging.debug(
|
||||||
|
"Received updated sensor value for %s: %s",
|
||||||
|
self.config.name,
|
||||||
|
self.last_measurements,
|
||||||
|
)
|
||||||
|
|
||||||
|
async def _on_mqtt_disconnected(self):
|
||||||
|
self.error_state = "MQTT Disconnected"
|
||||||
|
self.last_measurements = {}
|
||||||
|
|
||||||
|
async def initialize(self) -> bool:
|
||||||
|
await super().initialize()
|
||||||
|
try:
|
||||||
|
self.mqtt.subscribe_topic(
|
||||||
|
self.state_topic,
|
||||||
|
self._on_state_update,
|
||||||
|
self.qos,
|
||||||
|
)
|
||||||
|
self.error_state = None
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
self.error_state = str(e)
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class Sensors:
|
||||||
|
__sensor_types: Dict[str, Type[BaseSensor]] = {"MQTT": MQTTSensor}
|
||||||
|
|
||||||
|
def __init__(self, config: ConfigHelper) -> None:
|
||||||
|
self.server = config.get_server()
|
||||||
|
self.store_size = config.getint("sensor_store_size", 1200)
|
||||||
|
prefix_sections = config.get_prefix_sections("sensor")
|
||||||
|
self.sensors: Dict[str, BaseSensor] = {}
|
||||||
|
|
||||||
|
# Register timer to update sensor values in store
|
||||||
|
self.sensors_update_timer = self.server.get_event_loop().register_timer(
|
||||||
|
self._update_sensor_values
|
||||||
|
)
|
||||||
|
|
||||||
|
# Register endpoints
|
||||||
|
self.server.register_endpoint(
|
||||||
|
"/server/sensors/list",
|
||||||
|
["GET"],
|
||||||
|
self._handle_sensor_list_request,
|
||||||
|
)
|
||||||
|
self.server.register_endpoint(
|
||||||
|
"/server/sensors/info",
|
||||||
|
["GET"],
|
||||||
|
self._handle_sensor_info_request,
|
||||||
|
)
|
||||||
|
self.server.register_endpoint(
|
||||||
|
"/server/sensors/measurements",
|
||||||
|
["GET"],
|
||||||
|
self._handle_sensor_measurements_request,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Register notifications
|
||||||
|
self.server.register_notification(SENSOR_EVENT_NAME)
|
||||||
|
|
||||||
|
for section in prefix_sections:
|
||||||
|
cfg = config[section]
|
||||||
|
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
_, name = cfg.get_name().split(maxsplit=1)
|
||||||
|
except ValueError:
|
||||||
|
raise cfg.error(f"Invalid section name: {cfg.get_name()}")
|
||||||
|
|
||||||
|
logging.info(f"Configuring sensor: {name}")
|
||||||
|
|
||||||
|
sensor_type: str = cfg.get("type")
|
||||||
|
sensor_class: Optional[Type[BaseSensor]] = self.__sensor_types.get(
|
||||||
|
sensor_type.upper(), None
|
||||||
|
)
|
||||||
|
if sensor_class is None:
|
||||||
|
raise config.error(f"Unsupported sensor type: {sensor_type}")
|
||||||
|
|
||||||
|
self.sensors[name] = sensor_class(
|
||||||
|
name=name,
|
||||||
|
cfg=cfg,
|
||||||
|
store_size=self.store_size,
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
# Ensures that configuration errors are shown to the user
|
||||||
|
self.server.add_warning(
|
||||||
|
f"Failed to configure sensor [{cfg.get_name()}]\n{e}"
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
|
def _update_sensor_values(self, eventtime: float) -> float:
|
||||||
|
"""
|
||||||
|
Iterate through the sensors and store the last updated value.
|
||||||
|
"""
|
||||||
|
changed_data: Dict[str, Dict[str, Union[int, float]]] = {}
|
||||||
|
for sensor_name, sensor in self.sensors.items():
|
||||||
|
base_value = sensor.last_value
|
||||||
|
sensor._update_sensor_value(eventtime=eventtime)
|
||||||
|
|
||||||
|
# Notify if a change in sensor values was detected
|
||||||
|
if base_value != sensor.last_value:
|
||||||
|
changed_data[sensor_name] = sensor.last_value
|
||||||
|
if changed_data:
|
||||||
|
self.server.send_event(SENSOR_EVENT_NAME, changed_data)
|
||||||
|
|
||||||
|
return eventtime + SENSOR_UPDATE_TIME
|
||||||
|
|
||||||
|
async def component_init(self) -> None:
|
||||||
|
try:
|
||||||
|
logging.debug("Initializing sensor component")
|
||||||
|
for sensor in self.sensors.values():
|
||||||
|
if not await sensor.initialize():
|
||||||
|
self.server.add_warning(
|
||||||
|
f"Sensor '{sensor.get_name()}' failed to initialize"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.sensors_update_timer.start()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logging.exception(e)
|
||||||
|
|
||||||
|
async def _handle_sensor_list_request(
|
||||||
|
self, web_request: WebRequest
|
||||||
|
) -> Dict[str, Dict[str, Any]]:
|
||||||
|
output = {
|
||||||
|
"sensors": {
|
||||||
|
key: sensor.get_sensor_info() for key, sensor in self.sensors.items()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return output
|
||||||
|
|
||||||
|
async def _handle_sensor_info_request(
|
||||||
|
self, web_request: WebRequest
|
||||||
|
) -> Dict[str, Any]:
|
||||||
|
sensor_name: str = web_request.get_str("sensor")
|
||||||
|
if sensor_name not in self.sensors:
|
||||||
|
raise self.server.error(f"No valid sensor named {sensor_name}")
|
||||||
|
|
||||||
|
sensor = self.sensors[sensor_name]
|
||||||
|
|
||||||
|
return sensor.get_sensor_info()
|
||||||
|
|
||||||
|
async def _handle_sensor_measurements_request(
|
||||||
|
self, web_request: WebRequest
|
||||||
|
) -> Dict[str, Dict[str, Any]]:
|
||||||
|
sensor_name: str = web_request.get_str("sensor", "")
|
||||||
|
if sensor_name and sensor_name not in self.sensors:
|
||||||
|
raise self.server.error(f"No valid sensor named {sensor_name}")
|
||||||
|
|
||||||
|
output = {
|
||||||
|
key: sensor.get_sensor_measurements()
|
||||||
|
for key, sensor in self.sensors.items()
|
||||||
|
if sensor_name is "" or key == sensor_name
|
||||||
|
}
|
||||||
|
|
||||||
|
return output
|
||||||
|
|
||||||
|
def close(self) -> None:
|
||||||
|
self.sensors_update_timer.stop()
|
||||||
|
for sensor in self.sensors.values():
|
||||||
|
sensor.close()
|
||||||
|
|
||||||
|
|
||||||
|
def load_component(config: ConfigHelper) -> Sensors:
|
||||||
|
return Sensors(config)
|
Loading…
x
Reference in New Issue
Block a user