Skip to content

Commit e29c895

Browse files
authored
Added virtual number (#137)
1 parent b0d1f4f commit e29c895

File tree

3 files changed

+184
-0
lines changed

3 files changed

+184
-0
lines changed

custom_components/virtual/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
Platform.FAN,
5656
Platform.LIGHT,
5757
Platform.LOCK,
58+
Platform.NUMBER,
5859
Platform.SENSOR,
5960
Platform.SWITCH,
6061
Platform.VALVE

custom_components/virtual/const.py

+2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
CONF_CLASS = "class"
2222
CONF_INITIAL_AVAILABILITY = "initial_availability"
2323
CONF_INITIAL_VALUE = "initial_value"
24+
CONF_MAX = "max"
25+
CONF_MIN = "min"
2426
CONF_NAME = "name"
2527
CONF_OPEN_CLOSE_DURATION = "open_close_duration"
2628
CONF_OPEN_CLOSE_TICK = "open_close_tick"

custom_components/virtual/number.py

+181
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
"""
2+
This component provides support for a virtual number.
3+
4+
"""
5+
6+
import logging
7+
import voluptuous as vol
8+
from collections.abc import Callable
9+
10+
import homeassistant.helpers.config_validation as cv
11+
from homeassistant.components.number import (
12+
ATTR_MAX, ATTR_MIN, DOMAIN as PLATFORM_DOMAIN,
13+
NumberDeviceClass
14+
)
15+
from homeassistant.config_entries import ConfigEntry
16+
from homeassistant.const import (
17+
ATTR_ENTITY_ID,
18+
ATTR_DEVICE_CLASS,
19+
ATTR_UNIT_OF_MEASUREMENT,
20+
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
21+
CONCENTRATION_PARTS_PER_MILLION,
22+
CONF_UNIT_OF_MEASUREMENT,
23+
LIGHT_LUX,
24+
PERCENTAGE,
25+
SIGNAL_STRENGTH_DECIBELS,
26+
UnitOfApparentPower,
27+
UnitOfElectricCurrent,
28+
UnitOfElectricPotential,
29+
UnitOfEnergy,
30+
UnitOfFrequency,
31+
UnitOfPower,
32+
UnitOfPressure,
33+
UnitOfReactivePower,
34+
UnitOfVolume,
35+
)
36+
from homeassistant.core import HomeAssistant
37+
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA
38+
from homeassistant.helpers.entity import Entity
39+
from homeassistant.helpers.entity_platform import AddEntitiesCallback
40+
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
41+
42+
from . import get_entity_from_domain, get_entity_configs
43+
from .const import *
44+
from .entity import VirtualEntity, virtual_schema
45+
46+
47+
_LOGGER = logging.getLogger(__name__)
48+
49+
DEPENDENCIES = [COMPONENT_DOMAIN]
50+
51+
DEFAULT_NUMBER_VALUE = "0"
52+
53+
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(virtual_schema(DEFAULT_NUMBER_VALUE, {
54+
vol.Optional(CONF_CLASS): cv.string,
55+
vol.Required(CONF_MIN): vol.Coerce(float),
56+
vol.Required(CONF_MAX): vol.Coerce(float),
57+
vol.Optional(CONF_UNIT_OF_MEASUREMENT, default=""): cv.string,
58+
}))
59+
NUMBER_SCHEMA = vol.Schema(virtual_schema(DEFAULT_NUMBER_VALUE, {
60+
vol.Optional(CONF_CLASS): cv.string,
61+
vol.Required(CONF_MIN): vol.Coerce(float),
62+
vol.Required(CONF_MAX): vol.Coerce(float),
63+
vol.Optional(CONF_UNIT_OF_MEASUREMENT, default=""): cv.string,
64+
}))
65+
66+
UNITS_OF_MEASUREMENT = {
67+
NumberDeviceClass.APPARENT_POWER: UnitOfApparentPower.VOLT_AMPERE, # apparent power (VA)
68+
NumberDeviceClass.BATTERY: PERCENTAGE, # % of battery that is left
69+
NumberDeviceClass.CO: CONCENTRATION_PARTS_PER_MILLION, # ppm of CO concentration
70+
NumberDeviceClass.CO2: CONCENTRATION_PARTS_PER_MILLION, # ppm of CO2 concentration
71+
NumberDeviceClass.HUMIDITY: PERCENTAGE, # % of humidity in the air
72+
NumberDeviceClass.ILLUMINANCE: LIGHT_LUX, # current light level (lx/lm)
73+
NumberDeviceClass.NITROGEN_DIOXIDE: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, # µg/m³ of nitrogen dioxide
74+
NumberDeviceClass.NITROGEN_MONOXIDE: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, # µg/m³ of nitrogen monoxide
75+
NumberDeviceClass.NITROUS_OXIDE: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, # µg/m³ of nitrogen oxide
76+
NumberDeviceClass.OZONE: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, # µg/m³ of ozone
77+
NumberDeviceClass.PM1: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, # µg/m³ of PM1
78+
NumberDeviceClass.PM10: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, # µg/m³ of PM10
79+
NumberDeviceClass.PM25: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, # µg/m³ of PM2.5
80+
NumberDeviceClass.SIGNAL_STRENGTH: SIGNAL_STRENGTH_DECIBELS, # signal strength (dB/dBm)
81+
NumberDeviceClass.SULPHUR_DIOXIDE: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, # µg/m³ of sulphur dioxide
82+
NumberDeviceClass.TEMPERATURE: "C", # temperature (C/F)
83+
NumberDeviceClass.PRESSURE: UnitOfPressure.HPA, # pressure (hPa/mbar)
84+
NumberDeviceClass.POWER: UnitOfPower.KILO_WATT, # power (W/kW)
85+
NumberDeviceClass.CURRENT: UnitOfElectricCurrent.AMPERE, # current (A)
86+
NumberDeviceClass.ENERGY: UnitOfEnergy.KILO_WATT_HOUR, # energy (Wh/kWh/MWh)
87+
NumberDeviceClass.FREQUENCY: UnitOfFrequency.GIGAHERTZ, # energy (Hz/kHz/MHz/GHz)
88+
NumberDeviceClass.POWER_FACTOR: PERCENTAGE, # power factor (no unit, min: -1.0, max: 1.0)
89+
NumberDeviceClass.REACTIVE_POWER: UnitOfReactivePower.VOLT_AMPERE_REACTIVE, # reactive power (var)
90+
NumberDeviceClass.VOLATILE_ORGANIC_COMPOUNDS: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, # µg/m³ of vocs
91+
NumberDeviceClass.VOLTAGE: UnitOfElectricPotential.VOLT, # voltage (V)
92+
NumberDeviceClass.GAS: UnitOfVolume.CUBIC_METERS, # gas (m³)
93+
}
94+
95+
96+
async def async_setup_platform(
97+
hass: HomeAssistant,
98+
config: ConfigType,
99+
async_add_entities: AddEntitiesCallback,
100+
_discovery_info: DiscoveryInfoType | None = None,
101+
) -> None:
102+
if hass.data[COMPONENT_CONFIG].get(CONF_YAML_CONFIG, False):
103+
_LOGGER.debug("setting up old config...")
104+
105+
sensors = [VirtualNumber(config, True)]
106+
async_add_entities(sensors, True)
107+
108+
109+
async def async_setup_entry(
110+
hass: HomeAssistant,
111+
entry: ConfigEntry,
112+
async_add_entities: Callable[[list], None],
113+
) -> None:
114+
_LOGGER.debug("setting up the entries...")
115+
116+
entities = []
117+
for entity in get_entity_configs(hass, entry.data[ATTR_GROUP_NAME], PLATFORM_DOMAIN):
118+
entity = NUMBER_SCHEMA(entity)
119+
entities.append(VirtualNumber(entity, False))
120+
async_add_entities(entities)
121+
122+
123+
class VirtualNumber(VirtualEntity, Entity):
124+
"""An implementation of a Virtual Number."""
125+
126+
def __init__(self, config, old_style: bool):
127+
"""Initialize an Virtual Number."""
128+
super().__init__(config, PLATFORM_DOMAIN, old_style)
129+
130+
self._attr_device_class = config.get(CONF_CLASS)
131+
132+
self.min_value = config.get(CONF_MIN)
133+
self.max_value = config.get(CONF_MAX)
134+
135+
# Set unit of measurement
136+
self._attr_unit_of_measurement = config.get(CONF_UNIT_OF_MEASUREMENT)
137+
if not self._attr_unit_of_measurement and self._attr_device_class in UNITS_OF_MEASUREMENT.keys():
138+
self._attr_unit_of_measurement = UNITS_OF_MEASUREMENT[self._attr_device_class]
139+
140+
_LOGGER.info(f"VirtualSensor: {self.name} created")
141+
142+
def convert_to_native_value(self, value: float) -> float:
143+
return value
144+
145+
@property
146+
def native_min_value(self):
147+
return self.min_value
148+
149+
@property
150+
def native_max_value(self):
151+
return self.max_value
152+
153+
def _create_state(self, config):
154+
super()._create_state(config)
155+
156+
self._attr_state = config.get(CONF_INITIAL_VALUE)
157+
158+
def _restore_state(self, state, config):
159+
super()._restore_state(state, config)
160+
161+
self._attr_state = state.state
162+
163+
def _update_attributes(self):
164+
super()._update_attributes()
165+
self._attr_extra_state_attributes.update({
166+
name: value for name, value in (
167+
(ATTR_DEVICE_CLASS, self._attr_device_class),
168+
(ATTR_UNIT_OF_MEASUREMENT, self._attr_unit_of_measurement),
169+
(ATTR_MIN, self.min_value),
170+
(ATTR_MAX, self.max_value)
171+
) if value is not None
172+
})
173+
174+
async def async_set_native_value(self, value: float) -> None:
175+
"""Set new value."""
176+
await self.hass.async_add_executor_job(self.set, value)
177+
178+
def set(self, value) -> None:
179+
_LOGGER.debug(f"set {self.name} to {value}")
180+
self._attr_state = value
181+
#self.async_schedule_update_ha_state()

0 commit comments

Comments
 (0)