Skip to content

Commit b8cacad

Browse files
committed
Add support for binary sensors
Closes: #65
1 parent 47051fb commit b8cacad

2 files changed

Lines changed: 146 additions & 7 deletions

File tree

README.md

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,47 @@ Tested against Home Asisstant 2025.8.3.
1010

1111
## Options
1212

13+
[Device class (binary sensor)]: https://www.home-assistant.io/integrations/binary_sensor/#device-class
14+
[Device class (sensor)]: https://www.home-assistant.io/integrations/sensor/#device-class
15+
[PromQL]: https://prometheus.io/docs/prometheus/latest/querying/basics/
16+
[State class]: https://developers.home-assistant.io/docs/core/entity/sensor/#available-state-classes
17+
[Template]: https://www.home-assistant.io/docs/configuration/templating/
18+
[Unique ID]: https://www.home-assistant.io/faq/unique_id/
19+
20+
### Binary Sensor
21+
22+
| Name | Type | Default | Description |
23+
|---------------------|--------|----------|--------------------------------|
24+
| name | string | required | Friendly name |
25+
| unique_id | string | optional | [Unique ID] |
26+
| expr | string | required | [PromQL] expression |
27+
| value_template | string | required | [Template] |
28+
| device_class | string | optional | [Device class (binary sensor)] |
29+
30+
### Sensor
31+
1332
| Name | Type | Default | Description |
1433
|---------------------|--------|----------|-------------------------|
1534
| name | string | required | Friendly name |
16-
| expr | string | required | [PromQL] expression |
1735
| unique_id | string | optional | [Unique ID] |
36+
| expr | string | required | [PromQL] expression |
1837
| unit_of_measurement | string | optional | Unit of the measurement |
19-
| device_class | string | optional | [Device class] |
38+
| device_class | string | optional | [Device class (sensor)] |
2039
| state_class | string | optional | [State class] |
2140

22-
[PromQL]: https://prometheus.io/docs/prometheus/latest/querying/basics/
23-
[Unique ID]: https://www.home-assistant.io/faq/unique_id/
24-
[Device class]: https://www.home-assistant.io/integrations/sensor/#device-class
25-
[State class]: https://developers.home-assistant.io/docs/core/entity/sensor/#available-state-classes
26-
2741
## Example usage
2842

2943
```yaml
44+
binary_sensor:
45+
- platform: prometheus_sensor
46+
url: http://localhost:9090
47+
queries:
48+
- name: Front Door
49+
unique_id: front_door_open
50+
expr: front_door_open
51+
value_template: "{{ value == 1 }}"
52+
device_class: door
53+
3054
sensor:
3155
- platform: prometheus_sensor
3256
url: http://localhost:9090
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
"""Prometheus Binary Sensor component."""
2+
3+
from __future__ import annotations
4+
5+
from typing import TYPE_CHECKING, Final
6+
7+
import voluptuous as vol
8+
9+
from homeassistant.components.binary_sensor import (
10+
PLATFORM_SCHEMA as BINARY_SENSOR_PLATFORM_SCHEMA,
11+
BinarySensorDeviceClass,
12+
BinarySensorEntity,
13+
)
14+
from homeassistant.const import (
15+
CONF_DEVICE_CLASS,
16+
CONF_NAME,
17+
CONF_UNIQUE_ID,
18+
CONF_URL,
19+
CONF_VALUE_TEMPLATE,
20+
)
21+
from homeassistant.helpers.aiohttp_client import async_get_clientsession
22+
import homeassistant.helpers.config_validation as cv
23+
24+
from . import Prometheus
25+
26+
if TYPE_CHECKING:
27+
from homeassistant.core import HomeAssistant
28+
from homeassistant.helpers.entity_platform import AddEntitiesCallback
29+
from homeassistant.helpers.template import Template
30+
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
31+
32+
from . import QueryResult
33+
34+
from .const import CONF_EXPR, CONF_QUERIES, DEFAULT_URL, SCAN_INTERVAL as SCAN_INTERVAL
35+
36+
_QUERY_SCHEMA: Final = vol.Schema(
37+
{
38+
vol.Required(CONF_NAME): cv.string,
39+
vol.Optional(CONF_UNIQUE_ID): cv.string,
40+
vol.Required(CONF_EXPR): cv.string,
41+
vol.Required(CONF_VALUE_TEMPLATE): cv.template,
42+
vol.Optional(CONF_DEVICE_CLASS): vol.Coerce(BinarySensorDeviceClass),
43+
}
44+
)
45+
46+
PLATFORM_SCHEMA: Final = BINARY_SENSOR_PLATFORM_SCHEMA.extend(
47+
{
48+
vol.Optional(CONF_URL, default=DEFAULT_URL): cv.string, # type: ignore
49+
vol.Required(CONF_QUERIES): [_QUERY_SCHEMA],
50+
}
51+
)
52+
53+
54+
async def async_setup_platform(
55+
hass: HomeAssistant,
56+
config: ConfigType,
57+
async_add_entities: AddEntitiesCallback,
58+
discovery_info: DiscoveryInfoType | None = None,
59+
):
60+
"""Set up the sensor platform."""
61+
session = async_get_clientsession(hass)
62+
url = config[CONF_URL]
63+
prometheus = Prometheus(url, session)
64+
65+
async_add_entities(
66+
new_entities=[
67+
PrometheusBinarySensor(
68+
prometheus=prometheus,
69+
unique_id=query.get(CONF_UNIQUE_ID),
70+
device_name=query[CONF_NAME],
71+
expression=query[CONF_EXPR],
72+
value_template=query.get(CONF_VALUE_TEMPLATE),
73+
device_class=query.get(CONF_DEVICE_CLASS),
74+
)
75+
for query in config[CONF_QUERIES]
76+
],
77+
update_before_add=True,
78+
)
79+
80+
81+
class PrometheusBinarySensor(BinarySensorEntity):
82+
"""Sensor entity representing the result of a PromQL expression."""
83+
84+
def __init__(
85+
self,
86+
*,
87+
prometheus: Prometheus,
88+
unique_id: str | None,
89+
device_name: str,
90+
expression: str,
91+
value_template: Template,
92+
device_class: BinarySensorDeviceClass | None,
93+
) -> None:
94+
"""Initialize the sensor."""
95+
self._prometheus: Prometheus = prometheus
96+
self._expression = expression
97+
self._value_template = value_template
98+
99+
self._attr_device_class = device_class
100+
self._attr_name = device_name
101+
self._attr_unique_id = unique_id
102+
103+
async def async_update(self) -> None:
104+
"""Update state by executing query."""
105+
result: QueryResult = await self._prometheus.query(self._expression)
106+
self._attr_available = result.error is None
107+
108+
render_result = self._value_template.async_render(
109+
variables=dict(value=result.value)
110+
)
111+
112+
if render_result is not None:
113+
self._attr_is_on = bool(render_result)
114+
else:
115+
self._attr_is_on = None

0 commit comments

Comments
 (0)