Add Charger as switch. Add attributes for charge rate and status. No service yet

This commit is contained in:
magico13
2022-05-14 17:36:43 -04:00
parent a4e93de06a
commit e686f785b3
2 changed files with 93 additions and 38 deletions

View File

@@ -1,49 +1,62 @@
import logging
from datetime import datetime
from typing import Callable, List
from typing import Any, Mapping
from homeassistant.const import ENERGY_WATT_HOUR, POWER_WATT
from homeassistant.helpers import device_registry, entity_registry
from homeassistant.helpers.entity import DeviceInfo, Entity
from homeassistant.helpers.entity_registry import async_entries_for_device
from homeassistant.helpers.update_coordinator import (
CoordinatorEntity,
)
from homeassistant.util import dt
from .const import DOMAIN, VUE_DATA
from pyemvue import pyemvue
from pyemvue.device import VueDevice, ChargerDevice
from pyemvue.device import VueDevice
_LOGGER = logging.getLogger(__name__)
class EmporiaChargerEntity(Entity):
class EmporiaChargerEntity(CoordinatorEntity):
"""Emporia Charger Entity"""
def __init__(
self,
data,
coordinator,
vue: pyemvue.PyEmVue,
device: VueDevice,
units: str,
device_class: str,
enabled_default=True,
):
super().__init__(coordinator)
self._coordinator = coordinator
self._device = device
self.data = data
self._vue = vue
self._enabled_default = enabled_default
self._units = units
self._device_class = device_class
self._charger = device.ev_charger
self._name = device.device_name
self._attr_unit_of_measurement = units
self._attr_device_class = device_class
self._attr_name = device.device_name
@property
def entity_registry_enabled_default(self):
return self._enabled_default
@property
def name(self):
"""Name of the entity."""
return self._name
def extra_state_attributes(self) -> Mapping[str, Any]:
data = self._coordinator.data[self._device.device_gid]
if data:
return {
"charging_rate": data.charging_rate,
"max_charging_rate": data.max_charging_rate,
"status": data.status,
"message": data.message,
"fault_text": data.fault_text,
}
return None
@property
def unique_id(self) -> str:
@@ -54,29 +67,14 @@ class EmporiaChargerEntity(Entity):
def device_info(self):
"""Return the device information."""
return {
"identifiers": (DOMAIN, str(self._device.device_gid)),
"name": self._device.device_name,
"identifiers": {(DOMAIN, "{0}-1,2,3".format(self._device.device_gid))},
"name": self._device.device_name + "-1,2,3",
"model": self._device.model,
"sw_version": self._device.firmware,
"manufacturer": "Emporia",
}
@property
def native_unit_of_measurement(self):
"""Return the native unit of measurement of this entity, if any."""
return self._units
@property
def available(self):
"""Return True if entity is available."""
return self._device.connected
@property
def device_class(self):
"""Device class of sensor."""
return self._device_class
@property
def should_poll(self):
"""No polling needed."""
return False
return self._device

View File

@@ -3,9 +3,14 @@ from datetime import timedelta
import logging
import asyncio
from tkinter import UNITS
import async_timeout
from config.custom_components.emporia_vue.charger_entity import EmporiaChargerEntity
from homeassistant.components.switch import SwitchEntity
from homeassistant.components.switch import DEVICE_CLASS_OUTLET, SwitchEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import (
CoordinatorEntity,
DataUpdateCoordinator,
@@ -15,14 +20,18 @@ from homeassistant.helpers.update_coordinator import (
from .const import DOMAIN, VUE_DATA
from pyemvue import pyemvue
from pyemvue.device import OutletDevice
from pyemvue.device import OutletDevice, ChargerDevice
_LOGGER = logging.getLogger(__name__)
device_information = {} # data is the populated device objects
async def async_setup_entry(hass, config_entry, async_add_entities):
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
):
"""Set up the sensor platform."""
vue = hass.data[DOMAIN][config_entry.entry_id][VUE_DATA]
@@ -32,6 +41,9 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
if device.outlet is not None:
await loop.run_in_executor(None, vue.populate_device_properties, device)
device_information[device.device_gid] = device
elif device.ev_charger is not None:
await loop.run_in_executor(None, vue.populate_device_properties, device)
device_information[device.device_gid] = device
async def async_update_data():
"""Fetch data from API endpoint.
@@ -44,10 +56,15 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
# handled by the data update coordinator.
data = {}
loop = asyncio.get_event_loop()
# TODO: Swap from separate get_outlets/get_chargers to a single get_status
outlets = await loop.run_in_executor(None, vue.get_outlets)
if outlets:
for outlet in outlets:
data[outlet.device_gid] = outlet
chargers = await loop.run_in_executor(None, vue.get_chargers)
if chargers:
for charger in chargers:
data[charger.device_gid] = charger
return data
except Exception as err:
raise UpdateFailed(f"Error communicating with Emporia API: {err}")
@@ -59,15 +76,23 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
name="switch",
update_method=async_update_data,
# Polling interval. Will only be polled if there are subscribers.
update_interval=timedelta(minutes=5),
update_interval=timedelta(minutes=1),
)
await coordinator.async_refresh()
async_add_entities(
EmporiaOutletSwitch(coordinator, vue, id)
for idx, id in enumerate(coordinator.data)
)
switches = []
for _, gid in enumerate(coordinator.data):
if device_information[gid].outlet:
switches.append(EmporiaOutletSwitch(coordinator, vue, gid))
elif device_information[gid].ev_charger:
switches.append(
EmporiaChargerSwitch(
coordinator, vue, device_information[gid], None, DEVICE_CLASS_OUTLET
)
)
async_add_entities(switches)
class EmporiaOutletSwitch(CoordinatorEntity, SwitchEntity):
@@ -81,6 +106,7 @@ class EmporiaOutletSwitch(CoordinatorEntity, SwitchEntity):
self._device_gid = gid
self._device = device_information[gid]
self._name = f"Switch {self._device.device_name}"
self._attr_device_class = DEVICE_CLASS_OUTLET
@property
def name(self):
@@ -129,3 +155,34 @@ class EmporiaOutletSwitch(CoordinatorEntity, SwitchEntity):
"manufacturer": "Emporia"
# "via_device": self._device.device_gid # might be able to map the extender, nested outlets
}
class EmporiaChargerSwitch(EmporiaChargerEntity, SwitchEntity):
"""Representation of an Emporia Charger switch state"""
@property
def is_on(self):
"""Return the state of the switch."""
return self.coordinator.data[self._device.device_gid].charger_on
async def async_turn_on(self, **kwargs):
"""Turn the charger on."""
loop = asyncio.get_event_loop()
await loop.run_in_executor(
None,
self._vue.update_charger,
self._coordinator.data[self._device.device_gid],
True,
)
await self._coordinator.async_request_refresh()
async def async_turn_off(self, **kwargs):
"""Turn the charger off."""
loop = asyncio.get_event_loop()
await loop.run_in_executor(
None,
self._vue.update_charger,
self._coordinator.data[self._device.device_gid],
False,
)
await self._coordinator.async_request_refresh()