diff --git a/.coveragerc b/.coveragerc index 74fb5c11cec..33cbb0ed450 100644 --- a/.coveragerc +++ b/.coveragerc @@ -163,6 +163,7 @@ omit = homeassistant/components/doorbird/* homeassistant/components/dovado/* homeassistant/components/downloader/* + homeassistant/components/dsmr_reader/* homeassistant/components/dte_energy_bridge/sensor.py homeassistant/components/dublin_bus_transport/sensor.py homeassistant/components/duke_energy/sensor.py diff --git a/CODEOWNERS b/CODEOWNERS index 74916740253..d7155e8f917 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -78,6 +78,7 @@ homeassistant/components/device_automation/* @home-assistant/core homeassistant/components/digital_ocean/* @fabaff homeassistant/components/discogs/* @thibmaek homeassistant/components/doorbird/* @oblogic7 +homeassistant/components/dsmr_reader/* @depl0y homeassistant/components/dweet/* @fabaff homeassistant/components/ecobee/* @marthoc homeassistant/components/ecovacs/* @OverloadUT diff --git a/homeassistant/components/dsmr_reader/__init__.py b/homeassistant/components/dsmr_reader/__init__.py new file mode 100755 index 00000000000..946be91d1a5 --- /dev/null +++ b/homeassistant/components/dsmr_reader/__init__.py @@ -0,0 +1 @@ +"""The DSMR Reader component.""" diff --git a/homeassistant/components/dsmr_reader/definitions.py b/homeassistant/components/dsmr_reader/definitions.py new file mode 100644 index 00000000000..f4a36ebc346 --- /dev/null +++ b/homeassistant/components/dsmr_reader/definitions.py @@ -0,0 +1,243 @@ +"""Definitions for DSMR Reader sensors added to MQTT.""" + + +def dsmr_transform(value): + """Transform DSMR version value to right format.""" + return float(value) / 10 + + +def tariff_transform(value): + """Transform tariff from number to description.""" + if value == "1": + return "low" + return "high" + + +DEFINITIONS = { + "dsmr/reading/electricity_delivered_1": { + "name": "Low tariff usage", + "icon": "mdi:flash", + "unit": "kWh", + }, + "dsmr/reading/electricity_returned_1": { + "name": "Low tariff returned", + "icon": "mdi:flash-outline", + "unit": "kWh", + }, + "dsmr/reading/electricity_delivered_2": { + "name": "High tariff usage", + "icon": "mdi:flash", + "unit": "kWh", + }, + "dsmr/reading/electricity_returned_2": { + "name": "High tariff returned", + "icon": "mdi:flash-outline", + "unit": "kWh", + }, + "dsmr/reading/electricity_currently_delivered": { + "name": "Current power usage", + "icon": "mdi:flash", + "unit": "kW", + }, + "dsmr/reading/electricity_currently_returned": { + "name": "Current power return", + "icon": "mdi:flash-outline", + "unit": "kW", + }, + "dsmr/reading/phase_currently_delivered_l1": { + "name": "Current power usage L1", + "icon": "mdi:flash", + "unit": "kW", + }, + "dsmr/reading/phase_currently_delivered_l2": { + "name": "Current power usage L2", + "icon": "mdi:flash", + "unit": "kW", + }, + "dsmr/reading/phase_currently_delivered_l3": { + "name": "Current power usage L3", + "icon": "mdi:flash", + "unit": "kW", + }, + "dsmr/reading/phase_currently_returned_l1": { + "name": "Current power return L1", + "icon": "mdi:flash-outline", + "unit": "kW", + }, + "dsmr/reading/phase_currently_returned_l2": { + "name": "Current power return L2", + "icon": "mdi:flash-outline", + "unit": "kW", + }, + "dsmr/reading/phase_currently_returned_l3": { + "name": "Current power return L3", + "icon": "mdi:flash-outline", + "unit": "kW", + }, + "dsmr/reading/extra_device_delivered": { + "name": "Gas meter usage", + "icon": "mdi:fire", + "unit": "m3", + }, + "dsmr/reading/phase_voltage_l1": { + "name": "Current voltage L1", + "icon": "mdi:flash", + "unit": "V", + }, + "dsmr/reading/phase_voltage_l2": { + "name": "Current voltage L2", + "icon": "mdi:flash", + "unit": "V", + }, + "dsmr/reading/phase_voltage_l3": { + "name": "Current voltage L3", + "icon": "mdi:flash", + "unit": "V", + }, + "dsmr/consumption/gas/delivered": { + "name": "Gas usage", + "icon": "mdi:fire", + "unit": "m3", + }, + "dsmr/consumption/gas/currently_delivered": { + "name": "Current gas usage", + "icon": "mdi:fire", + "unit": "m3", + }, + "dsmr/consumption/gas/read_at": { + "name": "Gas meter read", + "icon": "mdi:clock", + "unit": "", + }, + "dsmr/day-consumption/electricity1": { + "name": "Low tariff usage", + "icon": "mdi:counter", + "unit": "kWh", + }, + "dsmr/day-consumption/electricity2": { + "name": "High tariff usage", + "icon": "mdi:counter", + "unit": "kWh", + }, + "dsmr/day-consumption/electricity1_returned": { + "name": "Low tariff return", + "icon": "mdi:counter", + "unit": "kWh", + }, + "dsmr/day-consumption/electricity2_returned": { + "name": "High tariff return", + "icon": "mdi:counter", + "unit": "kWh", + }, + "dsmr/day-consumption/electricity_merged": { + "name": "Power usage total", + "icon": "mdi:counter", + "unit": "kWh", + }, + "dsmr/day-consumption/electricity_returned_merged": { + "name": "Power return total", + "icon": "mdi:counter", + "unit": "kWh", + }, + "dsmr/day-consumption/electricity1_cost": { + "name": "Low tariff cost", + "icon": "mdi:currency-eur", + "unit": "€", + }, + "dsmr/day-consumption/electricity2_cost": { + "name": "High tariff cost", + "icon": "mdi:currency-eur", + "unit": "€", + }, + "dsmr/day-consumption/electricity_cost_merged": { + "name": "Power total cost", + "icon": "mdi:currency-eur", + "unit": "€", + }, + "dsmr/day-consumption/gas": { + "name": "Gas usage", + "icon": "mdi:counter", + "unit": "m3", + }, + "dsmr/day-consumption/gas_cost": { + "name": "Gas cost", + "icon": "mdi:currency-eur", + "unit": "€", + }, + "dsmr/day-consumption/total_cost": { + "name": "Total cost", + "icon": "mdi:currency-eur", + "unit": "€", + }, + "dsmr/day-consumption/energy_supplier_price_electricity_delivered_1": { + "name": "Low tariff delivered price", + "icon": "mdi:currency-eur", + "unit": "€", + }, + "dsmr/day-consumption/energy_supplier_price_electricity_delivered_2": { + "name": "High tariff delivered price", + "icon": "mdi:currency-eur", + "unit": "€", + }, + "dsmr/day-consumption/energy_supplier_price_electricity_returned_1": { + "name": "Low tariff returned price", + "icon": "mdi:currency-eur", + "unit": "€", + }, + "dsmr/day-consumption/energy_supplier_price_electricity_returned_2": { + "name": "High tariff returned price", + "icon": "mdi:currency-eur", + "unit": "€", + }, + "dsmr/day-consumption/energy_supplier_price_gas": { + "name": "Gas price", + "icon": "mdi:currency-eur", + "unit": "€", + }, + "dsmr/meter-stats/dsmr_version": { + "name": "DSMR version", + "icon": "mdi:alert-circle", + "transform": dsmr_transform, + }, + "dsmr/meter-stats/electricity_tariff": { + "name": "Electricity tariff", + "icon": "mdi:flash", + "transform": tariff_transform, + }, + "dsmr/meter-stats/power_failure_count": { + "name": "Power failure count", + "icon": "mdi:flash", + }, + "dsmr/meter-stats/long_power_failure_count": { + "name": "Long power failure count", + "icon": "mdi:flash", + }, + "dsmr/meter-stats/voltage_sag_count_l1": { + "name": "Voltage sag L1", + "icon": "mdi:flash", + }, + "dsmr/meter-stats/voltage_sag_count_l2": { + "name": "Voltage sag L2", + "icon": "mdi:flash", + }, + "dsmr/meter-stats/voltage_sag_count_l3": { + "name": "Voltage sag L3", + "icon": "mdi:flash", + }, + "dsmr/meter-stats/voltage_swell_count_l1": { + "name": "Voltage swell L1", + "icon": "mdi:flash", + }, + "dsmr/meter-stats/voltage_swell_count_l2": { + "name": "Voltage swell L2", + "icon": "mdi:flash", + }, + "dsmr/meter-stats/voltage_swell_count_l3": { + "name": "Voltage swell L3", + "icon": "mdi:flash", + }, + "dsmr/meter-stats/rejected_telegrams": { + "name": "Rejected telegrams", + "icon": "mdi:flash", + }, +} diff --git a/homeassistant/components/dsmr_reader/manifest.json b/homeassistant/components/dsmr_reader/manifest.json new file mode 100755 index 00000000000..aa108d8bf9a --- /dev/null +++ b/homeassistant/components/dsmr_reader/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "dsmr_reader", + "name": "DSMR Reader", + "documentation": "https://www.home-assistant.io/components/dsmr_reader", + "requirements": [], + "dependencies": [ + "mqtt" + ], + "codeowners": [ + "@depl0y" + ] +} diff --git a/homeassistant/components/dsmr_reader/sensor.py b/homeassistant/components/dsmr_reader/sensor.py new file mode 100755 index 00000000000..c7efba9ddde --- /dev/null +++ b/homeassistant/components/dsmr_reader/sensor.py @@ -0,0 +1,87 @@ +"""Support for DSMR Reader through MQTT.""" +import logging + +from homeassistant.components import mqtt +from homeassistant.core import callback +from homeassistant.helpers.entity import Entity +from homeassistant.util import slugify + +from .definitions import DEFINITIONS + +_LOGGER = logging.getLogger(__name__) + +DOMAIN = "dsmr_reader" + + +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): + """Set up DSMR Reader sensors.""" + + sensors = [] + for topic in DEFINITIONS: + sensors.append(DSMRSensor(topic)) + + async_add_entities(sensors) + + +class DSMRSensor(Entity): + """Representation of a DSMR sensor that is updated via MQTT.""" + + def __init__(self, topic): + """Initialize the sensor.""" + + self._definition = DEFINITIONS[topic] + + self._entity_id = slugify(topic.replace("/", "_")) + self._topic = topic + + self._name = self._definition["name"] + self._unit_of_measurement = ( + self._definition["unit"] if "unit" in self._definition else "" + ) + self._icon = self._definition["icon"] if "icon" in self._definition else None + self._transform = ( + self._definition["transform"] if "transform" in self._definition else None + ) + + self._state = None + + async def async_added_to_hass(self): + """Subscribe to MQTT events.""" + + @callback + def message_received(message): + """Handle new MQTT messages.""" + + if self._transform is not None: + self._state = self._transform(message.payload) + else: + self._state = message.payload + + self.async_schedule_update_ha_state() + + return await mqtt.async_subscribe(self.hass, self._topic, message_received, 1) + + @property + def name(self): + """Return the name of the sensor supplied in constructor.""" + return self._name + + @property + def entity_id(self): + """Return the entity ID for this sensor.""" + return f"sensor.{self._entity_id}" + + @property + def state(self): + """Return the current state of the entity.""" + return self._state + + @property + def unit_of_measurement(self): + """Return the unit_of_measurement of this sensor.""" + return self._unit_of_measurement + + @property + def icon(self): + """Return the icon of this sensor.""" + return self._icon