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