Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 35 additions & 2 deletions custom_components/skykettle/kettle_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ def __init__(self, mac, key, persistent=True, adapter=None, hass=None, model=Non
self._colors = {}
self._disposed = False
self._last_data = None
self._prev_energy_wh = None
self._prev_energy_timestamp = None
self._power_w = None

async def command(self, command, params=[]):
if self._disposed:
Expand Down Expand Up @@ -90,13 +93,20 @@ async def _connect(self):
if self._disposed:
raise DisposedError()
if self._client and self._client.is_connected: return
self._device = bluetooth.async_ble_device_from_address(self.hass, self._mac)
self._device = bluetooth.async_ble_device_from_address(
self.hass, self._mac, connectable=True
)
if not self._device:
raise IOError("Device not found")
_LOGGER.debug("Connecting to the Kettle...")
self._client = await establish_connection(
BleakClientWithServiceCache,
self._device,
self._device.name or "Unknown Device",
max_attempts=3
max_attempts=3,
ble_device_callback=lambda: bluetooth.async_ble_device_from_address(
self.hass, self._mac, connectable=True
),
)
_LOGGER.debug("Connected to the Kettle")
await self._client.start_notify(KettleConnection.UUID_RX, self._rx_callback)
Expand Down Expand Up @@ -221,6 +231,25 @@ async def update(self, tries=MAX_TRIES, force_stats=False, extra_action=None, co
if self._last_get_stats + KettleConnection.STATS_INTERVAL < monotonic() or force_stats:
self._last_get_stats = monotonic()
self._stats = await self.get_stats()
# Compute power from energy delta
now = monotonic()
if self._stats and self._stats.energy_wh is not None:
energy_wh = self._stats.energy_wh
if self._prev_energy_wh is not None and self._prev_energy_timestamp is not None:
if energy_wh >= self._prev_energy_wh:
elapsed_hours = (now - self._prev_energy_timestamp) / 3600.0
if elapsed_hours > 0:
self._power_w = (energy_wh - self._prev_energy_wh) / elapsed_hours
else:
self._power_w = 0.0
else:
self._power_w = None
else:
self._power_w = None
self._prev_energy_wh = energy_wh
self._prev_energy_timestamp = now
Comment thread
kai-zer-ru marked this conversation as resolved.
else:
self._power_w = None
self._light_switch_boil = await self.get_light_switch(SkyKettle.LIGHT_BOIL)
self._light_switch_sync = await self.get_light_switch(SkyKettle.LIGHT_SYNC)
self._lamp_auto_off_hours = await self.get_lamp_auto_off_hours()
Expand Down Expand Up @@ -473,6 +502,10 @@ def energy_wh(self):
if not self._stats: return None
return self._stats.energy_wh

@property
def power_w(self):
return self._power_w

@property
def heater_on_count(self):
if not self._stats: return None
Expand Down
24 changes: 21 additions & 3 deletions custom_components/skykettle/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from homeassistant.components.sensor import (SensorDeviceClass, SensorEntity,
SensorStateClass)
from homeassistant.const import (CONF_FRIENDLY_NAME, PERCENTAGE, UnitOfEnergy,
UnitOfTime)
UnitOfPower, UnitOfTime)
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import EntityCategory

Expand All @@ -17,6 +17,7 @@
SENSOR_TYPE_WATER_FRESHNESS = "water_freshness"
SENSOR_TYPE_SUCCESS_RATE = "success_rate"
SENSOR_TYPE_ENERGY = "energy"
SENSOR_TYPE_POWER = "power"
SENSOR_TYPE_ONTIME = "ontime"
SENSOR_TYPE_HEATER_ON_COUNT = "heater_on_count"
SENSOR_TYPE_USER_ON_COUNT = "user_on_count"
Expand All @@ -31,6 +32,7 @@ async def async_setup_entry(hass, entry, async_add_entities):
if model_code in [SkyKettle.MODELS_4]: # RK-G2xxS, RK-M13xS, RK-M21xS, RK-M223S but not sure
async_add_entities([
SkySensor(hass, entry, SENSOR_TYPE_ENERGY),
SkySensor(hass, entry, SENSOR_TYPE_POWER),
SkySensor(hass, entry, SENSOR_TYPE_ONTIME),
SkySensor(hass, entry, SENSOR_TYPE_HEATER_ON_COUNT),
SkySensor(hass, entry, SENSOR_TYPE_USER_ON_COUNT),
Expand Down Expand Up @@ -82,6 +84,8 @@ def name(self):
"""Name of the entity."""
if self.sensor_type == SENSOR_TYPE_ENERGY:
return (FRIENDLY_NAME + " " + self.entry.data.get(CONF_FRIENDLY_NAME, "")).strip() + " total energy consumed"
if self.sensor_type == SENSOR_TYPE_POWER:
return (FRIENDLY_NAME + " " + self.entry.data.get(CONF_FRIENDLY_NAME, "")).strip() + " current power"
if self.sensor_type == SENSOR_TYPE_ONTIME:
return (FRIENDLY_NAME + " " + self.entry.data.get(CONF_FRIENDLY_NAME, "")).strip() + " total work time"
if self.sensor_type == SENSOR_TYPE_HEATER_ON_COUNT:
Expand All @@ -103,6 +107,8 @@ def icon(self):
return "mdi:dip-switch"
if self.sensor_type == SENSOR_TYPE_WATER_FRESHNESS:
return "mdi:water-sync"
if self.sensor_type == SENSOR_TYPE_POWER:
return "mdi:flash"
if self.sensor_type == SENSOR_TYPE_SUCCESS_RATE:
return "mdi:bluetooth-connect"
return None
Expand All @@ -111,6 +117,8 @@ def icon(self):
def available(self):
if self.sensor_type == SENSOR_TYPE_ENERGY:
return self.kettle.available and self.kettle.energy_wh != None
if self.sensor_type == SENSOR_TYPE_POWER:
return self.kettle.available and self.kettle.energy_wh is not None
Comment thread
kai-zer-ru marked this conversation as resolved.
if self.sensor_type == SENSOR_TYPE_ONTIME:
return self.kettle.available and self.kettle.ontime != None
if self.sensor_type == SENSOR_TYPE_HEATER_ON_COUNT:
Expand All @@ -132,6 +140,8 @@ def entity_category(self):
def device_class(self):
if self.sensor_type == SENSOR_TYPE_ENERGY:
return SensorDeviceClass.ENERGY
if self.sensor_type == SENSOR_TYPE_POWER:
return SensorDeviceClass.POWER
if self.sensor_type == SENSOR_TYPE_ONTIME:
return SensorDeviceClass.DURATION
if self.sensor_type == SENSOR_TYPE_WATER_FRESHNESS:
Expand All @@ -142,6 +152,8 @@ def device_class(self):
def state_class(self):
if self.sensor_type == SENSOR_TYPE_ENERGY:
return SensorStateClass.TOTAL_INCREASING
if self.sensor_type == SENSOR_TYPE_POWER:
return SensorStateClass.MEASUREMENT
if self.sensor_type == SENSOR_TYPE_ONTIME:
return SensorStateClass.TOTAL_INCREASING
if self.sensor_type == SENSOR_TYPE_HEATER_ON_COUNT:
Expand All @@ -156,7 +168,9 @@ def state_class(self):
@property
def native_unit_of_measurement(self):
if self.sensor_type == SENSOR_TYPE_ENERGY:
return UnitOfEnergy.WATT_HOUR
return UnitOfEnergy.KILO_WATT_HOUR
Comment thread
kai-zer-ru marked this conversation as resolved.
if self.sensor_type == SENSOR_TYPE_POWER:
return UnitOfPower.WATT
if self.sensor_type == SENSOR_TYPE_ONTIME:
return UnitOfTime.SECONDS
if self.sensor_type == SENSOR_TYPE_WATER_FRESHNESS:
Expand All @@ -168,7 +182,11 @@ def native_unit_of_measurement(self):
@property
def native_value(self):
if self.sensor_type == SENSOR_TYPE_ENERGY:
return self.kettle.energy_wh
e = self.kettle.energy_wh
return round(e / 1000.0, 2) if e is not None else None
if self.sensor_type == SENSOR_TYPE_POWER:
pw = self.kettle.power_w
return pw
if self.sensor_type == SENSOR_TYPE_ONTIME:
return self.kettle.ontime.total_seconds()
if self.sensor_type == SENSOR_TYPE_HEATER_ON_COUNT:
Expand Down
1 change: 1 addition & 0 deletions custom_components/skykettle/water_heater.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ def extra_state_attributes(self):
"ontime_seconds": self.kettle.ontime.total_seconds() if self.kettle.ontime else None,
"ontime_string": str(self.kettle.ontime),
"energy_wh": self.kettle.energy_wh,
Comment thread
kai-zer-ru marked this conversation as resolved.
"power_w": self.kettle.power_w,
"heater_on_count": self.kettle.heater_on_count,
"user_on_count": self.kettle.user_on_count,
"sound_enabled": self.kettle.sound_enabled,
Expand Down