Update plugwise to async and config_flow sensor part (#36219)
Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
parent
5d7720832b
commit
d0fedad000
7 changed files with 439 additions and 83 deletions
|
@ -606,6 +606,7 @@ omit =
|
||||||
homeassistant/components/plex/sensor.py
|
homeassistant/components/plex/sensor.py
|
||||||
homeassistant/components/plugwise/__init__.py
|
homeassistant/components/plugwise/__init__.py
|
||||||
homeassistant/components/plugwise/climate.py
|
homeassistant/components/plugwise/climate.py
|
||||||
|
homeassistant/components/plugwise/sensor.py
|
||||||
homeassistant/components/plum_lightpad/*
|
homeassistant/components/plum_lightpad/*
|
||||||
homeassistant/components/pocketcasts/sensor.py
|
homeassistant/components/pocketcasts/sensor.py
|
||||||
homeassistant/components/point/*
|
homeassistant/components/point/*
|
||||||
|
|
|
@ -3,13 +3,14 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
import logging
|
import logging
|
||||||
|
from typing import Dict
|
||||||
|
|
||||||
from Plugwise_Smile.Smile import Smile
|
from Plugwise_Smile.Smile import Smile
|
||||||
import async_timeout
|
import async_timeout
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.exceptions import ConfigEntryNotReady
|
from homeassistant.exceptions import ConfigEntryNotReady
|
||||||
from homeassistant.helpers import device_registry as dr
|
from homeassistant.helpers import device_registry as dr
|
||||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
|
@ -22,7 +23,8 @@ CONFIG_SCHEMA = vol.Schema({DOMAIN: vol.Schema({})}, extra=vol.ALLOW_EXTRA)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
ALL_PLATFORMS = ["climate"]
|
SENSOR_PLATFORMS = ["sensor"]
|
||||||
|
ALL_PLATFORMS = ["climate", "sensor"]
|
||||||
|
|
||||||
|
|
||||||
async def async_setup(hass: HomeAssistant, config: dict):
|
async def async_setup(hass: HomeAssistant, config: dict):
|
||||||
|
@ -100,7 +102,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
sw_version=api.smile_version[0],
|
sw_version=api.smile_version[0],
|
||||||
)
|
)
|
||||||
|
|
||||||
for component in ALL_PLATFORMS:
|
platforms = ALL_PLATFORMS
|
||||||
|
|
||||||
|
single_master_thermostat = api.single_master_thermostat()
|
||||||
|
if single_master_thermostat is None:
|
||||||
|
platforms = SENSOR_PLATFORMS
|
||||||
|
|
||||||
|
for component in platforms:
|
||||||
hass.async_create_task(
|
hass.async_create_task(
|
||||||
hass.config_entries.async_forward_entry_setup(entry, component)
|
hass.config_entries.async_forward_entry_setup(entry, component)
|
||||||
)
|
)
|
||||||
|
@ -127,11 +135,17 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
|
||||||
class SmileGateway(Entity):
|
class SmileGateway(Entity):
|
||||||
"""Represent Smile Gateway."""
|
"""Represent Smile Gateway."""
|
||||||
|
|
||||||
def __init__(self, api, coordinator):
|
def __init__(self, api, coordinator, name, dev_id):
|
||||||
"""Initialise the sensor."""
|
"""Initialise the gateway."""
|
||||||
self._api = api
|
self._api = api
|
||||||
self._coordinator = coordinator
|
self._coordinator = coordinator
|
||||||
|
self._name = name
|
||||||
|
self._dev_id = dev_id
|
||||||
|
|
||||||
self._unique_id = None
|
self._unique_id = None
|
||||||
|
self._model = None
|
||||||
|
|
||||||
|
self._entity_name = self._name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def unique_id(self):
|
def unique_id(self):
|
||||||
|
@ -148,11 +162,40 @@ class SmileGateway(Entity):
|
||||||
"""Return True if entity is available."""
|
"""Return True if entity is available."""
|
||||||
return self._coordinator.last_update_success
|
return self._coordinator.last_update_success
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
"""Return the name of the entity, if any."""
|
||||||
|
if not self._name:
|
||||||
|
return None
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def device_info(self) -> Dict[str, any]:
|
||||||
|
"""Return the device information."""
|
||||||
|
|
||||||
|
device_information = {
|
||||||
|
"identifiers": {(DOMAIN, self._dev_id)},
|
||||||
|
"name": self._entity_name,
|
||||||
|
"manufacturer": "Plugwise",
|
||||||
|
}
|
||||||
|
|
||||||
|
if self._model is not None:
|
||||||
|
device_information["model"] = self._model.replace("_", " ").title()
|
||||||
|
|
||||||
|
if self._dev_id != self._api.gateway_id:
|
||||||
|
device_information["via_device"] = (DOMAIN, self._api.gateway_id)
|
||||||
|
|
||||||
|
return device_information
|
||||||
|
|
||||||
async def async_added_to_hass(self):
|
async def async_added_to_hass(self):
|
||||||
"""Subscribe to updates."""
|
"""Subscribe to updates."""
|
||||||
self.async_on_remove(self._coordinator.async_add_listener(self._process_data))
|
self._async_process_data()
|
||||||
|
self.async_on_remove(
|
||||||
|
self._coordinator.async_add_listener(self._async_process_data)
|
||||||
|
)
|
||||||
|
|
||||||
def _process_data(self):
|
@callback
|
||||||
|
def _async_process_data(self):
|
||||||
"""Interpret and process API data."""
|
"""Interpret and process API data."""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
"""Plugwise Climate component for Home Assistant."""
|
"""Plugwise Climate component for Home Assistant."""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from typing import Dict
|
|
||||||
|
|
||||||
from Plugwise_Smile.Smile import Smile
|
from Plugwise_Smile.Smile import Smile
|
||||||
|
|
||||||
|
@ -17,16 +16,10 @@ from homeassistant.components.climate.const import (
|
||||||
SUPPORT_TARGET_TEMPERATURE,
|
SUPPORT_TARGET_TEMPERATURE,
|
||||||
)
|
)
|
||||||
from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS
|
from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS
|
||||||
|
from homeassistant.core import callback
|
||||||
|
|
||||||
from . import SmileGateway
|
from . import SmileGateway
|
||||||
from .const import (
|
from .const import DEFAULT_MAX_TEMP, DEFAULT_MIN_TEMP, DOMAIN, SCHEDULE_OFF, SCHEDULE_ON
|
||||||
DEFAULT_MAX_TEMP,
|
|
||||||
DEFAULT_MIN_TEMP,
|
|
||||||
DOMAIN,
|
|
||||||
SCHEDULE_OFF,
|
|
||||||
SCHEDULE_ON,
|
|
||||||
THERMOSTAT_ICON,
|
|
||||||
)
|
|
||||||
|
|
||||||
HVAC_MODES_HEAT_ONLY = [HVAC_MODE_HEAT, HVAC_MODE_AUTO]
|
HVAC_MODES_HEAT_ONLY = [HVAC_MODE_HEAT, HVAC_MODE_AUTO]
|
||||||
HVAC_MODES_HEAT_COOL = [HVAC_MODE_HEAT_COOL, HVAC_MODE_AUTO]
|
HVAC_MODES_HEAT_COOL = [HVAC_MODE_HEAT_COOL, HVAC_MODE_AUTO]
|
||||||
|
@ -47,20 +40,20 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
"zone_thermostat",
|
"zone_thermostat",
|
||||||
"thermostatic_radiator_valve",
|
"thermostatic_radiator_valve",
|
||||||
]
|
]
|
||||||
all_entities = api.get_all_devices()
|
all_devices = api.get_all_devices()
|
||||||
|
|
||||||
for dev_id, device in all_entities.items():
|
for dev_id, device_properties in all_devices.items():
|
||||||
|
|
||||||
if device["class"] not in thermostat_classes:
|
if device_properties["class"] not in thermostat_classes:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
thermostat = PwThermostat(
|
thermostat = PwThermostat(
|
||||||
api,
|
api,
|
||||||
coordinator,
|
coordinator,
|
||||||
device["name"],
|
device_properties["name"],
|
||||||
dev_id,
|
dev_id,
|
||||||
device["location"],
|
device_properties["location"],
|
||||||
device["class"],
|
device_properties["class"],
|
||||||
DEFAULT_MIN_TEMP,
|
DEFAULT_MIN_TEMP,
|
||||||
DEFAULT_MAX_TEMP,
|
DEFAULT_MAX_TEMP,
|
||||||
)
|
)
|
||||||
|
@ -77,11 +70,9 @@ class PwThermostat(SmileGateway, ClimateEntity):
|
||||||
self, api, coordinator, name, dev_id, loc_id, model, min_temp, max_temp
|
self, api, coordinator, name, dev_id, loc_id, model, min_temp, max_temp
|
||||||
):
|
):
|
||||||
"""Set up the Plugwise API."""
|
"""Set up the Plugwise API."""
|
||||||
super().__init__(api, coordinator)
|
super().__init__(api, coordinator, name, dev_id)
|
||||||
|
|
||||||
self._api = api
|
self._api = api
|
||||||
self._name = name
|
|
||||||
self._dev_id = dev_id
|
|
||||||
self._loc_id = loc_id
|
self._loc_id = loc_id
|
||||||
self._model = model
|
self._model = model
|
||||||
self._min_temp = min_temp
|
self._min_temp = min_temp
|
||||||
|
@ -92,9 +83,9 @@ class PwThermostat(SmileGateway, ClimateEntity):
|
||||||
self._preset_mode = None
|
self._preset_mode = None
|
||||||
self._presets = None
|
self._presets = None
|
||||||
self._presets_list = None
|
self._presets_list = None
|
||||||
self._boiler_state = None
|
|
||||||
self._heating_state = None
|
self._heating_state = None
|
||||||
self._cooling_state = None
|
self._cooling_state = None
|
||||||
|
self._compressor_state = None
|
||||||
self._dhw_state = None
|
self._dhw_state = None
|
||||||
self._hvac_mode = None
|
self._hvac_mode = None
|
||||||
self._schema_names = None
|
self._schema_names = None
|
||||||
|
@ -111,43 +102,16 @@ class PwThermostat(SmileGateway, ClimateEntity):
|
||||||
def hvac_action(self):
|
def hvac_action(self):
|
||||||
"""Return the current action."""
|
"""Return the current action."""
|
||||||
if self._single_thermostat:
|
if self._single_thermostat:
|
||||||
if self._heating_state or self._boiler_state:
|
if self._heating_state:
|
||||||
return CURRENT_HVAC_HEAT
|
return CURRENT_HVAC_HEAT
|
||||||
if self._cooling_state:
|
if self._cooling_state:
|
||||||
return CURRENT_HVAC_COOL
|
return CURRENT_HVAC_COOL
|
||||||
return CURRENT_HVAC_IDLE
|
return CURRENT_HVAC_IDLE
|
||||||
if self._heating_state is not None or self._boiler_state is not None:
|
if self._heating_state is not None:
|
||||||
if self._setpoint > self._temperature:
|
if self._setpoint > self._temperature:
|
||||||
return CURRENT_HVAC_HEAT
|
return CURRENT_HVAC_HEAT
|
||||||
return CURRENT_HVAC_IDLE
|
return CURRENT_HVAC_IDLE
|
||||||
|
|
||||||
@property
|
|
||||||
def name(self):
|
|
||||||
"""Return the name of the thermostat, if any."""
|
|
||||||
return self._name
|
|
||||||
|
|
||||||
@property
|
|
||||||
def device_info(self) -> Dict[str, any]:
|
|
||||||
"""Return the device information."""
|
|
||||||
|
|
||||||
device_information = {
|
|
||||||
"identifiers": {(DOMAIN, self._dev_id)},
|
|
||||||
"name": self._name,
|
|
||||||
"manufacturer": "Plugwise",
|
|
||||||
"model": self._model.replace("_", " ").title(),
|
|
||||||
}
|
|
||||||
|
|
||||||
if self._dev_id != self._api.gateway_id:
|
|
||||||
device_information["via_device"] = (DOMAIN, self._api.gateway_id)
|
|
||||||
del device_information["via_device"]
|
|
||||||
|
|
||||||
return device_information
|
|
||||||
|
|
||||||
@property
|
|
||||||
def icon(self):
|
|
||||||
"""Return the icon to use in the frontend."""
|
|
||||||
return THERMOSTAT_ICON
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def supported_features(self):
|
def supported_features(self):
|
||||||
"""Return the list of supported features."""
|
"""Return the list of supported features."""
|
||||||
|
@ -172,8 +136,8 @@ class PwThermostat(SmileGateway, ClimateEntity):
|
||||||
@property
|
@property
|
||||||
def hvac_modes(self):
|
def hvac_modes(self):
|
||||||
"""Return the available hvac modes list."""
|
"""Return the available hvac modes list."""
|
||||||
if self._heating_state is not None or self._boiler_state is not None:
|
if self._heating_state is not None:
|
||||||
if self._cooling_state is not None:
|
if self._compressor_state is not None:
|
||||||
return HVAC_MODES_HEAT_COOL
|
return HVAC_MODES_HEAT_COOL
|
||||||
return HVAC_MODES_HEAT_ONLY
|
return HVAC_MODES_HEAT_ONLY
|
||||||
|
|
||||||
|
@ -258,7 +222,8 @@ class PwThermostat(SmileGateway, ClimateEntity):
|
||||||
except Smile.PlugwiseError:
|
except Smile.PlugwiseError:
|
||||||
_LOGGER.error("Error while communicating to device")
|
_LOGGER.error("Error while communicating to device")
|
||||||
|
|
||||||
def _process_data(self):
|
@callback
|
||||||
|
def _async_process_data(self):
|
||||||
"""Update the data for this climate device."""
|
"""Update the data for this climate device."""
|
||||||
climate_data = self._api.get_device_data(self._dev_id)
|
climate_data = self._api.get_device_data(self._dev_id)
|
||||||
heater_central_data = self._api.get_device_data(self._api.heater_id)
|
heater_central_data = self._api.get_device_data(self._api.heater_id)
|
||||||
|
@ -286,21 +251,18 @@ class PwThermostat(SmileGateway, ClimateEntity):
|
||||||
if "active_preset" in climate_data:
|
if "active_preset" in climate_data:
|
||||||
self._preset_mode = climate_data["active_preset"]
|
self._preset_mode = climate_data["active_preset"]
|
||||||
|
|
||||||
if "boiler_state" in heater_central_data:
|
if heater_central_data.get("heating_state") is not None:
|
||||||
if heater_central_data["boiler_state"] is not None:
|
self._heating_state = heater_central_data["heating_state"]
|
||||||
self._boiler_state = heater_central_data["boiler_state"]
|
if heater_central_data.get("cooling_state") is not None:
|
||||||
if "heating_state" in heater_central_data:
|
self._cooling_state = heater_central_data["cooling_state"]
|
||||||
if heater_central_data["heating_state"] is not None:
|
if heater_central_data.get("compressor_state") is not None:
|
||||||
self._heating_state = heater_central_data["heating_state"]
|
self._compressor_state = heater_central_data["compressor_state"]
|
||||||
if "cooling_state" in heater_central_data:
|
|
||||||
if heater_central_data["cooling_state"] is not None:
|
|
||||||
self._cooling_state = heater_central_data["cooling_state"]
|
|
||||||
|
|
||||||
if self._schema_status:
|
if self._schema_status:
|
||||||
self._hvac_mode = HVAC_MODE_AUTO
|
self._hvac_mode = HVAC_MODE_AUTO
|
||||||
elif self._heating_state is not None or self._boiler_state is not None:
|
elif self._heating_state is not None:
|
||||||
self._hvac_mode = HVAC_MODE_HEAT
|
self._hvac_mode = HVAC_MODE_HEAT
|
||||||
if self._cooling_state is not None:
|
if self._compressor_state is not None:
|
||||||
self._hvac_mode = HVAC_MODE_HEAT_COOL
|
self._hvac_mode = HVAC_MODE_HEAT_COOL
|
||||||
|
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
|
@ -47,7 +47,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
async def async_step_user(self, user_input=None):
|
async def async_step_user(self, user_input=None):
|
||||||
"""Handle the initial step."""
|
"""Handle the initial step."""
|
||||||
errors = {}
|
errors = {}
|
||||||
api = None
|
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
"""Constant for Plugwise component."""
|
"""Constant for Plugwise component."""
|
||||||
DOMAIN = "plugwise"
|
DOMAIN = "plugwise"
|
||||||
|
|
||||||
|
# Sensor mapping
|
||||||
|
SENSOR_MAP_MODEL = 0
|
||||||
|
SENSOR_MAP_UOM = 1
|
||||||
|
SENSOR_MAP_DEVICE_CLASS = 2
|
||||||
|
|
||||||
# Default directives
|
# Default directives
|
||||||
DEFAULT_NAME = "Smile"
|
DEFAULT_NAME = "Smile"
|
||||||
DEFAULT_USERNAME = "smile"
|
DEFAULT_USERNAME = "smile"
|
||||||
|
@ -10,8 +15,6 @@ DEFAULT_MIN_TEMP = 4
|
||||||
DEFAULT_MAX_TEMP = 30
|
DEFAULT_MAX_TEMP = 30
|
||||||
DEFAULT_SCAN_INTERVAL = {"thermostat": 60, "power": 10}
|
DEFAULT_SCAN_INTERVAL = {"thermostat": 60, "power": 10}
|
||||||
|
|
||||||
DEVICE_CLASS_GAS = "gas"
|
|
||||||
|
|
||||||
# Configuration directives
|
# Configuration directives
|
||||||
CONF_MIN_TEMP = "min_temp"
|
CONF_MIN_TEMP = "min_temp"
|
||||||
CONF_MAX_TEMP = "max_temp"
|
CONF_MAX_TEMP = "max_temp"
|
||||||
|
@ -22,21 +25,15 @@ CONF_SOLAR = "solar"
|
||||||
CONF_GAS = "gas"
|
CONF_GAS = "gas"
|
||||||
|
|
||||||
ATTR_ILLUMINANCE = "illuminance"
|
ATTR_ILLUMINANCE = "illuminance"
|
||||||
|
UNIT_LUMEN = "lm"
|
||||||
|
|
||||||
CURRENT_HVAC_DHW = "hot_water"
|
CURRENT_HVAC_DHW = "hot_water"
|
||||||
|
|
||||||
DEVICE_STATE = "device_state"
|
DEVICE_STATE = "device_state"
|
||||||
|
|
||||||
SCHEDULE_ON = "true"
|
SCHEDULE_ON = "true"
|
||||||
SCHEDULE_OFF = "false"
|
SCHEDULE_OFF = "false"
|
||||||
|
|
||||||
# Icons
|
|
||||||
SWITCH_ICON = "mdi:electric-switch"
|
|
||||||
THERMOSTAT_ICON = "mdi:thermometer"
|
|
||||||
WATER_ICON = "mdi:water-pump"
|
|
||||||
FLAME_ICON = "mdi:fire"
|
|
||||||
COOL_ICON = "mdi:snowflake"
|
COOL_ICON = "mdi:snowflake"
|
||||||
|
FLAME_ICON = "mdi:fire"
|
||||||
IDLE_ICON = "mdi:circle-off-outline"
|
IDLE_ICON = "mdi:circle-off-outline"
|
||||||
GAS_ICON = "mdi:fire"
|
|
||||||
POWER_ICON = "mdi:flash"
|
|
||||||
POWER_FAILURE_ICON = "mdi:flash-off"
|
|
||||||
SWELL_SAG_ICON = "mdi:pulse"
|
|
||||||
VALVE_ICON = "mdi:valve"
|
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
"name": "Plugwise",
|
"name": "Plugwise",
|
||||||
"documentation": "https://www.home-assistant.io/integrations/plugwise",
|
"documentation": "https://www.home-assistant.io/integrations/plugwise",
|
||||||
"requirements": ["Plugwise_Smile==0.2.10"],
|
"requirements": ["Plugwise_Smile==0.2.10"],
|
||||||
"dependencies": [],
|
|
||||||
"codeowners": ["@CoMPaTech", "@bouwew"],
|
"codeowners": ["@CoMPaTech", "@bouwew"],
|
||||||
"config_flow": true
|
"config_flow": true
|
||||||
}
|
}
|
||||||
|
|
354
homeassistant/components/plugwise/sensor.py
Normal file
354
homeassistant/components/plugwise/sensor.py
Normal file
|
@ -0,0 +1,354 @@
|
||||||
|
"""Plugwise Sensor component for Home Assistant."""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from homeassistant.const import (
|
||||||
|
DEVICE_CLASS_BATTERY,
|
||||||
|
DEVICE_CLASS_ILLUMINANCE,
|
||||||
|
DEVICE_CLASS_POWER,
|
||||||
|
DEVICE_CLASS_PRESSURE,
|
||||||
|
DEVICE_CLASS_TEMPERATURE,
|
||||||
|
ENERGY_KILO_WATT_HOUR,
|
||||||
|
ENERGY_WATT_HOUR,
|
||||||
|
POWER_WATT,
|
||||||
|
PRESSURE_BAR,
|
||||||
|
TEMP_CELSIUS,
|
||||||
|
UNIT_PERCENTAGE,
|
||||||
|
VOLUME_CUBIC_METERS,
|
||||||
|
)
|
||||||
|
from homeassistant.core import callback
|
||||||
|
from homeassistant.helpers.entity import Entity
|
||||||
|
|
||||||
|
from . import SmileGateway
|
||||||
|
from .const import (
|
||||||
|
COOL_ICON,
|
||||||
|
DEVICE_STATE,
|
||||||
|
DOMAIN,
|
||||||
|
FLAME_ICON,
|
||||||
|
IDLE_ICON,
|
||||||
|
SENSOR_MAP_DEVICE_CLASS,
|
||||||
|
SENSOR_MAP_MODEL,
|
||||||
|
SENSOR_MAP_UOM,
|
||||||
|
UNIT_LUMEN,
|
||||||
|
)
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
ATTR_TEMPERATURE = [
|
||||||
|
"Temperature",
|
||||||
|
TEMP_CELSIUS,
|
||||||
|
DEVICE_CLASS_TEMPERATURE,
|
||||||
|
]
|
||||||
|
ATTR_BATTERY_LEVEL = [
|
||||||
|
"Charge",
|
||||||
|
UNIT_PERCENTAGE,
|
||||||
|
DEVICE_CLASS_BATTERY,
|
||||||
|
]
|
||||||
|
ATTR_ILLUMINANCE = [
|
||||||
|
"Illuminance",
|
||||||
|
UNIT_LUMEN,
|
||||||
|
DEVICE_CLASS_ILLUMINANCE,
|
||||||
|
]
|
||||||
|
ATTR_PRESSURE = ["Pressure", PRESSURE_BAR, DEVICE_CLASS_PRESSURE]
|
||||||
|
|
||||||
|
TEMP_SENSOR_MAP = {
|
||||||
|
"setpoint": ATTR_TEMPERATURE,
|
||||||
|
"temperature": ATTR_TEMPERATURE,
|
||||||
|
"intended_boiler_temperature": ATTR_TEMPERATURE,
|
||||||
|
"temperature_difference": ATTR_TEMPERATURE,
|
||||||
|
"outdoor_temperature": ATTR_TEMPERATURE,
|
||||||
|
"water_temperature": ATTR_TEMPERATURE,
|
||||||
|
"return_temperature": ATTR_TEMPERATURE,
|
||||||
|
}
|
||||||
|
|
||||||
|
ENERGY_SENSOR_MAP = {
|
||||||
|
"electricity_consumed": ["Current Consumed Power", POWER_WATT, DEVICE_CLASS_POWER],
|
||||||
|
"electricity_produced": ["Current Produced Power", POWER_WATT, DEVICE_CLASS_POWER],
|
||||||
|
"electricity_consumed_interval": [
|
||||||
|
"Consumed Power Interval",
|
||||||
|
ENERGY_WATT_HOUR,
|
||||||
|
DEVICE_CLASS_POWER,
|
||||||
|
],
|
||||||
|
"electricity_produced_interval": [
|
||||||
|
"Produced Power Interval",
|
||||||
|
ENERGY_WATT_HOUR,
|
||||||
|
DEVICE_CLASS_POWER,
|
||||||
|
],
|
||||||
|
"electricity_consumed_off_peak_point": [
|
||||||
|
"Current Consumed Power (off peak)",
|
||||||
|
POWER_WATT,
|
||||||
|
DEVICE_CLASS_POWER,
|
||||||
|
],
|
||||||
|
"electricity_consumed_peak_point": [
|
||||||
|
"Current Consumed Power",
|
||||||
|
POWER_WATT,
|
||||||
|
DEVICE_CLASS_POWER,
|
||||||
|
],
|
||||||
|
"electricity_consumed_off_peak_cumulative": [
|
||||||
|
"Cumulative Consumed Power (off peak)",
|
||||||
|
ENERGY_KILO_WATT_HOUR,
|
||||||
|
DEVICE_CLASS_POWER,
|
||||||
|
],
|
||||||
|
"electricity_consumed_peak_cumulative": [
|
||||||
|
"Cumulative Consumed Power",
|
||||||
|
ENERGY_KILO_WATT_HOUR,
|
||||||
|
DEVICE_CLASS_POWER,
|
||||||
|
],
|
||||||
|
"electricity_produced_off_peak_point": [
|
||||||
|
"Current Consumed Power (off peak)",
|
||||||
|
POWER_WATT,
|
||||||
|
DEVICE_CLASS_POWER,
|
||||||
|
],
|
||||||
|
"electricity_produced_peak_point": [
|
||||||
|
"Current Consumed Power",
|
||||||
|
POWER_WATT,
|
||||||
|
DEVICE_CLASS_POWER,
|
||||||
|
],
|
||||||
|
"electricity_produced_off_peak_cumulative": [
|
||||||
|
"Cumulative Consumed Power (off peak)",
|
||||||
|
ENERGY_KILO_WATT_HOUR,
|
||||||
|
DEVICE_CLASS_POWER,
|
||||||
|
],
|
||||||
|
"electricity_produced_peak_cumulative": [
|
||||||
|
"Cumulative Consumed Power",
|
||||||
|
ENERGY_KILO_WATT_HOUR,
|
||||||
|
DEVICE_CLASS_POWER,
|
||||||
|
],
|
||||||
|
"gas_consumed_interval": ["Current Consumed Gas", VOLUME_CUBIC_METERS, None],
|
||||||
|
"gas_consumed_cumulative": ["Cumulative Consumed Gas", VOLUME_CUBIC_METERS, None],
|
||||||
|
"net_electricity_point": ["Current net Power", POWER_WATT, DEVICE_CLASS_POWER],
|
||||||
|
"net_electricity_cumulative": [
|
||||||
|
"Cumulative net Power",
|
||||||
|
ENERGY_KILO_WATT_HOUR,
|
||||||
|
DEVICE_CLASS_POWER,
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
MISC_SENSOR_MAP = {
|
||||||
|
"battery": ATTR_BATTERY_LEVEL,
|
||||||
|
"illuminance": ATTR_ILLUMINANCE,
|
||||||
|
"modulation_level": ["Heater Modulation Level", UNIT_PERCENTAGE, None],
|
||||||
|
"valve_position": ["Valve Position", UNIT_PERCENTAGE, None],
|
||||||
|
"water_pressure": ATTR_PRESSURE,
|
||||||
|
}
|
||||||
|
|
||||||
|
INDICATE_ACTIVE_LOCAL_DEVICE = [
|
||||||
|
"cooling_state",
|
||||||
|
"flame_state",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
|
"""Set up the Smile sensors from a config entry."""
|
||||||
|
api = hass.data[DOMAIN][config_entry.entry_id]["api"]
|
||||||
|
coordinator = hass.data[DOMAIN][config_entry.entry_id]["coordinator"]
|
||||||
|
|
||||||
|
entities = []
|
||||||
|
all_devices = api.get_all_devices()
|
||||||
|
single_thermostat = api.single_master_thermostat()
|
||||||
|
for dev_id, device_properties in all_devices.items():
|
||||||
|
data = api.get_device_data(dev_id)
|
||||||
|
for sensor, sensor_type in {
|
||||||
|
**TEMP_SENSOR_MAP,
|
||||||
|
**ENERGY_SENSOR_MAP,
|
||||||
|
**MISC_SENSOR_MAP,
|
||||||
|
}.items():
|
||||||
|
if sensor in data:
|
||||||
|
if data[sensor] is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if "power" in device_properties["types"]:
|
||||||
|
model = None
|
||||||
|
|
||||||
|
if "plug" in device_properties["types"]:
|
||||||
|
model = "Metered Switch"
|
||||||
|
|
||||||
|
entities.append(
|
||||||
|
PwPowerSensor(
|
||||||
|
api,
|
||||||
|
coordinator,
|
||||||
|
device_properties["name"],
|
||||||
|
dev_id,
|
||||||
|
sensor,
|
||||||
|
sensor_type,
|
||||||
|
model,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
entities.append(
|
||||||
|
PwThermostatSensor(
|
||||||
|
api,
|
||||||
|
coordinator,
|
||||||
|
device_properties["name"],
|
||||||
|
dev_id,
|
||||||
|
sensor,
|
||||||
|
sensor_type,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if single_thermostat is False:
|
||||||
|
for state in INDICATE_ACTIVE_LOCAL_DEVICE:
|
||||||
|
if state in data:
|
||||||
|
entities.append(
|
||||||
|
PwAuxDeviceSensor(
|
||||||
|
api,
|
||||||
|
coordinator,
|
||||||
|
device_properties["name"],
|
||||||
|
dev_id,
|
||||||
|
DEVICE_STATE,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
break
|
||||||
|
|
||||||
|
async_add_entities(entities, True)
|
||||||
|
|
||||||
|
|
||||||
|
class SmileSensor(SmileGateway):
|
||||||
|
"""Represent Smile Sensors."""
|
||||||
|
|
||||||
|
def __init__(self, api, coordinator, name, dev_id, sensor):
|
||||||
|
"""Initialise the sensor."""
|
||||||
|
super().__init__(api, coordinator, name, dev_id)
|
||||||
|
|
||||||
|
self._sensor = sensor
|
||||||
|
|
||||||
|
self._dev_class = None
|
||||||
|
self._state = None
|
||||||
|
self._unit_of_measurement = None
|
||||||
|
|
||||||
|
if dev_id == self._api.heater_id:
|
||||||
|
self._entity_name = "Auxiliary"
|
||||||
|
|
||||||
|
sensorname = sensor.replace("_", " ").title()
|
||||||
|
self._name = f"{self._entity_name} {sensorname}"
|
||||||
|
|
||||||
|
if dev_id == self._api.gateway_id:
|
||||||
|
self._entity_name = f"Smile {self._entity_name}"
|
||||||
|
|
||||||
|
self._unique_id = f"{dev_id}-{sensor}"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def device_class(self):
|
||||||
|
"""Device class of this entity."""
|
||||||
|
return self._dev_class
|
||||||
|
|
||||||
|
@property
|
||||||
|
def state(self):
|
||||||
|
"""Device class of this entity."""
|
||||||
|
return self._state
|
||||||
|
|
||||||
|
@property
|
||||||
|
def unit_of_measurement(self):
|
||||||
|
"""Return the unit of measurement of this entity, if any."""
|
||||||
|
return self._unit_of_measurement
|
||||||
|
|
||||||
|
|
||||||
|
class PwThermostatSensor(SmileSensor, Entity):
|
||||||
|
"""Thermostat and climate sensor entities."""
|
||||||
|
|
||||||
|
def __init__(self, api, coordinator, name, dev_id, sensor, sensor_type):
|
||||||
|
"""Set up the Plugwise API."""
|
||||||
|
super().__init__(api, coordinator, name, dev_id, sensor)
|
||||||
|
|
||||||
|
self._model = sensor_type[SENSOR_MAP_MODEL]
|
||||||
|
self._unit_of_measurement = sensor_type[SENSOR_MAP_UOM]
|
||||||
|
self._dev_class = sensor_type[SENSOR_MAP_DEVICE_CLASS]
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _async_process_data(self):
|
||||||
|
"""Update the entity."""
|
||||||
|
data = self._api.get_device_data(self._dev_id)
|
||||||
|
|
||||||
|
if not data:
|
||||||
|
_LOGGER.error("Received no data for device %s.", self._entity_name)
|
||||||
|
self.async_write_ha_state()
|
||||||
|
return
|
||||||
|
|
||||||
|
if data.get(self._sensor) is not None:
|
||||||
|
measurement = data[self._sensor]
|
||||||
|
if self._sensor == "battery" or self._sensor == "valve_position":
|
||||||
|
measurement = measurement * 100
|
||||||
|
if self._unit_of_measurement == UNIT_PERCENTAGE:
|
||||||
|
measurement = int(measurement)
|
||||||
|
self._state = measurement
|
||||||
|
|
||||||
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
|
||||||
|
class PwAuxDeviceSensor(SmileSensor, Entity):
|
||||||
|
"""Auxiliary sensor entities for the heating/cooling device."""
|
||||||
|
|
||||||
|
def __init__(self, api, coordinator, name, dev_id, sensor):
|
||||||
|
"""Set up the Plugwise API."""
|
||||||
|
super().__init__(api, coordinator, name, dev_id, sensor)
|
||||||
|
|
||||||
|
self._cooling_state = False
|
||||||
|
self._heating_state = False
|
||||||
|
self._icon = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def icon(self):
|
||||||
|
"""Return the icon to use in the frontend."""
|
||||||
|
return self._icon
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _async_process_data(self):
|
||||||
|
"""Update the entity."""
|
||||||
|
data = self._api.get_device_data(self._dev_id)
|
||||||
|
|
||||||
|
if not data:
|
||||||
|
_LOGGER.error("Received no data for device %s.", self._entity_name)
|
||||||
|
self.async_write_ha_state()
|
||||||
|
return
|
||||||
|
|
||||||
|
if data.get("heating_state") is not None:
|
||||||
|
self._heating_state = data["heating_state"]
|
||||||
|
if data.get("cooling_state") is not None:
|
||||||
|
self._cooling_state = data["cooling_state"]
|
||||||
|
|
||||||
|
self._state = "idle"
|
||||||
|
self._icon = IDLE_ICON
|
||||||
|
if self._heating_state:
|
||||||
|
self._state = "heating"
|
||||||
|
self._icon = FLAME_ICON
|
||||||
|
if self._cooling_state:
|
||||||
|
self._state = "cooling"
|
||||||
|
self._icon = COOL_ICON
|
||||||
|
|
||||||
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
|
||||||
|
class PwPowerSensor(SmileSensor, Entity):
|
||||||
|
"""Power sensor entities."""
|
||||||
|
|
||||||
|
def __init__(self, api, coordinator, name, dev_id, sensor, sensor_type, model):
|
||||||
|
"""Set up the Plugwise API."""
|
||||||
|
super().__init__(api, coordinator, name, dev_id, sensor)
|
||||||
|
|
||||||
|
self._model = model
|
||||||
|
if model is None:
|
||||||
|
self._model = sensor_type[SENSOR_MAP_MODEL]
|
||||||
|
|
||||||
|
self._unit_of_measurement = sensor_type[SENSOR_MAP_UOM]
|
||||||
|
self._dev_class = sensor_type[SENSOR_MAP_DEVICE_CLASS]
|
||||||
|
|
||||||
|
if dev_id == self._api.gateway_id:
|
||||||
|
self._model = "P1 DSMR"
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _async_process_data(self):
|
||||||
|
"""Update the entity."""
|
||||||
|
data = self._api.get_device_data(self._dev_id)
|
||||||
|
|
||||||
|
if not data:
|
||||||
|
_LOGGER.error("Received no data for device %s.", self._entity_name)
|
||||||
|
self.async_write_ha_state()
|
||||||
|
return
|
||||||
|
|
||||||
|
if data.get(self._sensor) is not None:
|
||||||
|
measurement = data[self._sensor]
|
||||||
|
if self._unit_of_measurement == ENERGY_KILO_WATT_HOUR:
|
||||||
|
measurement = int(measurement / 1000)
|
||||||
|
self._state = measurement
|
||||||
|
|
||||||
|
self.async_write_ha_state()
|
Loading…
Add table
Add a link
Reference in a new issue