340 lines
9.6 KiB
Python
340 lines
9.6 KiB
Python
"""Support for ZHA AnalogOutput cluster."""
|
|
import functools
|
|
import logging
|
|
|
|
from homeassistant.components.number import DOMAIN, NumberEntity
|
|
from homeassistant.core import callback
|
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
|
|
|
from .core import discovery
|
|
from .core.const import (
|
|
CHANNEL_ANALOG_OUTPUT,
|
|
DATA_ZHA,
|
|
DATA_ZHA_DISPATCHERS,
|
|
SIGNAL_ADD_ENTITIES,
|
|
SIGNAL_ATTR_UPDATED,
|
|
)
|
|
from .core.registries import ZHA_ENTITIES
|
|
from .entity import ZhaEntity
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
STRICT_MATCH = functools.partial(ZHA_ENTITIES.strict_match, DOMAIN)
|
|
|
|
|
|
UNITS = {
|
|
0: "Square-meters",
|
|
1: "Square-feet",
|
|
2: "Milliamperes",
|
|
3: "Amperes",
|
|
4: "Ohms",
|
|
5: "Volts",
|
|
6: "Kilo-volts",
|
|
7: "Mega-volts",
|
|
8: "Volt-amperes",
|
|
9: "Kilo-volt-amperes",
|
|
10: "Mega-volt-amperes",
|
|
11: "Volt-amperes-reactive",
|
|
12: "Kilo-volt-amperes-reactive",
|
|
13: "Mega-volt-amperes-reactive",
|
|
14: "Degrees-phase",
|
|
15: "Power-factor",
|
|
16: "Joules",
|
|
17: "Kilojoules",
|
|
18: "Watt-hours",
|
|
19: "Kilowatt-hours",
|
|
20: "BTUs",
|
|
21: "Therms",
|
|
22: "Ton-hours",
|
|
23: "Joules-per-kilogram-dry-air",
|
|
24: "BTUs-per-pound-dry-air",
|
|
25: "Cycles-per-hour",
|
|
26: "Cycles-per-minute",
|
|
27: "Hertz",
|
|
28: "Grams-of-water-per-kilogram-dry-air",
|
|
29: "Percent-relative-humidity",
|
|
30: "Millimeters",
|
|
31: "Meters",
|
|
32: "Inches",
|
|
33: "Feet",
|
|
34: "Watts-per-square-foot",
|
|
35: "Watts-per-square-meter",
|
|
36: "Lumens",
|
|
37: "Luxes",
|
|
38: "Foot-candles",
|
|
39: "Kilograms",
|
|
40: "Pounds-mass",
|
|
41: "Tons",
|
|
42: "Kilograms-per-second",
|
|
43: "Kilograms-per-minute",
|
|
44: "Kilograms-per-hour",
|
|
45: "Pounds-mass-per-minute",
|
|
46: "Pounds-mass-per-hour",
|
|
47: "Watts",
|
|
48: "Kilowatts",
|
|
49: "Megawatts",
|
|
50: "BTUs-per-hour",
|
|
51: "Horsepower",
|
|
52: "Tons-refrigeration",
|
|
53: "Pascals",
|
|
54: "Kilopascals",
|
|
55: "Bars",
|
|
56: "Pounds-force-per-square-inch",
|
|
57: "Centimeters-of-water",
|
|
58: "Inches-of-water",
|
|
59: "Millimeters-of-mercury",
|
|
60: "Centimeters-of-mercury",
|
|
61: "Inches-of-mercury",
|
|
62: "°C",
|
|
63: "°K",
|
|
64: "°F",
|
|
65: "Degree-days-Celsius",
|
|
66: "Degree-days-Fahrenheit",
|
|
67: "Years",
|
|
68: "Months",
|
|
69: "Weeks",
|
|
70: "Days",
|
|
71: "Hours",
|
|
72: "Minutes",
|
|
73: "Seconds",
|
|
74: "Meters-per-second",
|
|
75: "Kilometers-per-hour",
|
|
76: "Feet-per-second",
|
|
77: "Feet-per-minute",
|
|
78: "Miles-per-hour",
|
|
79: "Cubic-feet",
|
|
80: "Cubic-meters",
|
|
81: "Imperial-gallons",
|
|
82: "Liters",
|
|
83: "Us-gallons",
|
|
84: "Cubic-feet-per-minute",
|
|
85: "Cubic-meters-per-second",
|
|
86: "Imperial-gallons-per-minute",
|
|
87: "Liters-per-second",
|
|
88: "Liters-per-minute",
|
|
89: "Us-gallons-per-minute",
|
|
90: "Degrees-angular",
|
|
91: "Degrees-Celsius-per-hour",
|
|
92: "Degrees-Celsius-per-minute",
|
|
93: "Degrees-Fahrenheit-per-hour",
|
|
94: "Degrees-Fahrenheit-per-minute",
|
|
95: None,
|
|
96: "Parts-per-million",
|
|
97: "Parts-per-billion",
|
|
98: "%",
|
|
99: "Percent-per-second",
|
|
100: "Per-minute",
|
|
101: "Per-second",
|
|
102: "Psi-per-Degree-Fahrenheit",
|
|
103: "Radians",
|
|
104: "Revolutions-per-minute",
|
|
105: "Currency1",
|
|
106: "Currency2",
|
|
107: "Currency3",
|
|
108: "Currency4",
|
|
109: "Currency5",
|
|
110: "Currency6",
|
|
111: "Currency7",
|
|
112: "Currency8",
|
|
113: "Currency9",
|
|
114: "Currency10",
|
|
115: "Square-inches",
|
|
116: "Square-centimeters",
|
|
117: "BTUs-per-pound",
|
|
118: "Centimeters",
|
|
119: "Pounds-mass-per-second",
|
|
120: "Delta-Degrees-Fahrenheit",
|
|
121: "Delta-Degrees-Kelvin",
|
|
122: "Kilohms",
|
|
123: "Megohms",
|
|
124: "Millivolts",
|
|
125: "Kilojoules-per-kilogram",
|
|
126: "Megajoules",
|
|
127: "Joules-per-degree-Kelvin",
|
|
128: "Joules-per-kilogram-degree-Kelvin",
|
|
129: "Kilohertz",
|
|
130: "Megahertz",
|
|
131: "Per-hour",
|
|
132: "Milliwatts",
|
|
133: "Hectopascals",
|
|
134: "Millibars",
|
|
135: "Cubic-meters-per-hour",
|
|
136: "Liters-per-hour",
|
|
137: "Kilowatt-hours-per-square-meter",
|
|
138: "Kilowatt-hours-per-square-foot",
|
|
139: "Megajoules-per-square-meter",
|
|
140: "Megajoules-per-square-foot",
|
|
141: "Watts-per-square-meter-Degree-Kelvin",
|
|
142: "Cubic-feet-per-second",
|
|
143: "Percent-obscuration-per-foot",
|
|
144: "Percent-obscuration-per-meter",
|
|
145: "Milliohms",
|
|
146: "Megawatt-hours",
|
|
147: "Kilo-BTUs",
|
|
148: "Mega-BTUs",
|
|
149: "Kilojoules-per-kilogram-dry-air",
|
|
150: "Megajoules-per-kilogram-dry-air",
|
|
151: "Kilojoules-per-degree-Kelvin",
|
|
152: "Megajoules-per-degree-Kelvin",
|
|
153: "Newton",
|
|
154: "Grams-per-second",
|
|
155: "Grams-per-minute",
|
|
156: "Tons-per-hour",
|
|
157: "Kilo-BTUs-per-hour",
|
|
158: "Hundredths-seconds",
|
|
159: "Milliseconds",
|
|
160: "Newton-meters",
|
|
161: "Millimeters-per-second",
|
|
162: "Millimeters-per-minute",
|
|
163: "Meters-per-minute",
|
|
164: "Meters-per-hour",
|
|
165: "Cubic-meters-per-minute",
|
|
166: "Meters-per-second-per-second",
|
|
167: "Amperes-per-meter",
|
|
168: "Amperes-per-square-meter",
|
|
169: "Ampere-square-meters",
|
|
170: "Farads",
|
|
171: "Henrys",
|
|
172: "Ohm-meters",
|
|
173: "Siemens",
|
|
174: "Siemens-per-meter",
|
|
175: "Teslas",
|
|
176: "Volts-per-degree-Kelvin",
|
|
177: "Volts-per-meter",
|
|
178: "Webers",
|
|
179: "Candelas",
|
|
180: "Candelas-per-square-meter",
|
|
181: "Kelvins-per-hour",
|
|
182: "Kelvins-per-minute",
|
|
183: "Joule-seconds",
|
|
185: "Square-meters-per-Newton",
|
|
186: "Kilogram-per-cubic-meter",
|
|
187: "Newton-seconds",
|
|
188: "Newtons-per-meter",
|
|
189: "Watts-per-meter-per-degree-Kelvin",
|
|
}
|
|
|
|
ICONS = {
|
|
0: "mdi:temperature-celsius",
|
|
1: "mdi:water-percent",
|
|
2: "mdi:gauge",
|
|
3: "mdi:speedometer",
|
|
4: "mdi:percent",
|
|
5: "mdi:air-filter",
|
|
6: "mdi:fan",
|
|
7: "mdi:flash",
|
|
8: "mdi:current-ac",
|
|
9: "mdi:flash",
|
|
10: "mdi:flash",
|
|
11: "mdi:flash",
|
|
12: "mdi:counter",
|
|
13: "mdi:thermometer-lines",
|
|
14: "mdi:timer",
|
|
}
|
|
|
|
|
|
async def async_setup_entry(hass, config_entry, async_add_entities):
|
|
"""Set up the Zigbee Home Automation Analog Output from config entry."""
|
|
entities_to_create = hass.data[DATA_ZHA][DOMAIN]
|
|
|
|
unsub = async_dispatcher_connect(
|
|
hass,
|
|
SIGNAL_ADD_ENTITIES,
|
|
functools.partial(
|
|
discovery.async_add_entities,
|
|
async_add_entities,
|
|
entities_to_create,
|
|
update_before_add=False,
|
|
),
|
|
)
|
|
hass.data[DATA_ZHA][DATA_ZHA_DISPATCHERS].append(unsub)
|
|
|
|
|
|
@STRICT_MATCH(channel_names=CHANNEL_ANALOG_OUTPUT)
|
|
class ZhaNumber(ZhaEntity, NumberEntity):
|
|
"""Representation of a ZHA Number entity."""
|
|
|
|
def __init__(self, unique_id, zha_device, channels, **kwargs):
|
|
"""Init this entity."""
|
|
super().__init__(unique_id, zha_device, channels, **kwargs)
|
|
self._analog_output_channel = self.cluster_channels.get(CHANNEL_ANALOG_OUTPUT)
|
|
|
|
async def async_added_to_hass(self):
|
|
"""Run when about to be added to hass."""
|
|
await super().async_added_to_hass()
|
|
self.async_accept_signal(
|
|
self._analog_output_channel, SIGNAL_ATTR_UPDATED, self.async_set_state
|
|
)
|
|
|
|
@property
|
|
def value(self):
|
|
"""Return the current value."""
|
|
return self._analog_output_channel.present_value
|
|
|
|
@property
|
|
def min_value(self):
|
|
"""Return the minimum value."""
|
|
min_present_value = self._analog_output_channel.min_present_value
|
|
if min_present_value is not None:
|
|
return min_present_value
|
|
return 0
|
|
|
|
@property
|
|
def max_value(self):
|
|
"""Return the maximum value."""
|
|
max_present_value = self._analog_output_channel.max_present_value
|
|
if max_present_value is not None:
|
|
return max_present_value
|
|
return 1023
|
|
|
|
@property
|
|
def step(self):
|
|
"""Return the value step."""
|
|
resolution = self._analog_output_channel.resolution
|
|
if resolution is not None:
|
|
return resolution
|
|
return super().step
|
|
|
|
@property
|
|
def name(self):
|
|
"""Return the name of the number entity."""
|
|
description = self._analog_output_channel.description
|
|
if description is not None and len(description) > 0:
|
|
return f"{super().name} {description}"
|
|
return super().name
|
|
|
|
@property
|
|
def icon(self):
|
|
"""Return the icon to be used for this entity."""
|
|
application_type = self._analog_output_channel.application_type
|
|
if application_type is not None:
|
|
return ICONS.get(application_type >> 16, super().icon)
|
|
return super().icon
|
|
|
|
@property
|
|
def unit_of_measurement(self):
|
|
"""Return the unit the value is expressed in."""
|
|
engineering_units = self._analog_output_channel.engineering_units
|
|
return UNITS.get(engineering_units)
|
|
|
|
@callback
|
|
def async_set_state(self, attr_id, attr_name, value):
|
|
"""Handle value update from channel."""
|
|
self.async_write_ha_state()
|
|
|
|
async def async_set_value(self, value):
|
|
"""Update the current value from HA."""
|
|
num_value = float(value)
|
|
if await self._analog_output_channel.async_set_present_value(num_value):
|
|
self.async_write_ha_state()
|
|
|
|
async def async_update(self):
|
|
"""Attempt to retrieve the state of the entity."""
|
|
await super().async_update()
|
|
_LOGGER.debug("polling current state")
|
|
if self._analog_output_channel:
|
|
value = await self._analog_output_channel.get_attribute_value(
|
|
"present_value", from_cache=False
|
|
)
|
|
_LOGGER.debug("read value=%s", value)
|