mirror of
https://github.com/magico13/ha-emporia-vue.git
synced 2026-01-09 20:37:59 -05:00
Add Charger as switch. Add attributes for charge rate and status. No service yet
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user