Skip to content
Open
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
5 changes: 5 additions & 0 deletions docs/api-reference/controller/mixins/consumptionh.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
ConsumptionHMixin
-----------------

.. autoclass:: meross_iot.controller.mixins.consumption.ConsumptionHMixin
:members:
3 changes: 2 additions & 1 deletion docs/api-reference/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ API Reference
stats
controller/device/base
controller/device/channel
controller/mixins/consumption
controller/mixins/consumptionx
controller/mixins/consumptionh
controller/mixins/diffuserlight
controller/mixins/diffuserspray
controller/mixins/dnd
Expand Down
46 changes: 44 additions & 2 deletions meross_iot/controller/mixins/consumption.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ async def async_get_daily_power_consumption(self,
timeout: Optional[float] = None,
*args, **kwargs) -> List[dict]:
"""
Returns the power consumption registered by this device.
Returns the daily power consumption registered by this device.

:param channel: channel to read data from

Expand Down Expand Up @@ -58,7 +58,7 @@ async def async_get_daily_power_consumption(self,
timeout: Optional[float] = None,
*args, **kwargs) -> List[dict]:
"""
Returns the power consumption registered by this device.
Returns the daily power consumption registered by this device.

:param channel: channel to read data from

Expand All @@ -77,4 +77,46 @@ async def async_get_daily_power_consumption(self,
'total_consumption_kwh': float(x.get('value'))/1000
} for x in data]

return res

class ConsumptionHMixin(object):
_execute_command: callable

def __init__(self, device_uuid: str,
manager,
**kwargs):
super().__init__(device_uuid=device_uuid, manager=manager, **kwargs)

async def async_get_hourly_power_consumption(self,
channel=0,
timeout: Optional[float] = None,
*args, **kwargs) -> List[dict]:
"""
Returns the hourly power consumption registered by this device.

:param channel: channel to read data from

:return: the historical consumption data
"""

payload = {
"consumptionH": [
{
"channel": channel
}
]}

# TODO: returning a nice PowerConsumtpionReport object rather than a list of dict?
result = await self._execute_command(method="GET",
namespace=Namespace.CONTROL_CONSUMPTIONH,
payload=payload,
timeout=timeout)
data = result.get('consumptionH')

# Parse the json data into nice-python native objects
res = [{
'datetime': datetime.fromtimestamp(x.get('timestamp')),
'total_consumption_kwh': float(x.get('value'))/1000
} for x in data[0]["data"]]

return res
3 changes: 2 additions & 1 deletion meross_iot/device_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from meross_iot.controller.device import BaseDevice, HubDevice, GenericSubDevice
from meross_iot.controller.mixins.alarm import AlarmMixin
from meross_iot.controller.mixins.consumption import ConsumptionXMixin, ConsumptionMixin
from meross_iot.controller.mixins.consumption import ConsumptionXMixin, ConsumptionMixin, ConsumptionHMixin
from meross_iot.controller.mixins.diffuser_light import DiffuserLightMixin
from meross_iot.controller.mixins.diffuser_spray import DiffuserSprayMixin
from meross_iot.controller.mixins.dnd import SystemDndMixin
Expand Down Expand Up @@ -39,6 +39,7 @@
Namespace.CONTROL_TOGGLE.value: ToggleMixin,
Namespace.CONTROL_CONSUMPTIONX.value: ConsumptionXMixin,
Namespace.CONTROL_CONSUMPTION.value: ConsumptionMixin,
Namespace.CONTROL_CONSUMPTIONH.value: ConsumptionHMixin,
Namespace.CONTROL_ELECTRICITY.value: ElectricityMixin,
Namespace.CONTROL_ALARM.value: AlarmMixin,

Expand Down
1 change: 1 addition & 0 deletions meross_iot/model/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ class Namespace(Enum):
CONTROL_ELECTRICITY = 'Appliance.Control.Electricity'
CONTROL_CONSUMPTION = 'Appliance.Control.Consumption'
CONTROL_CONSUMPTIONX = 'Appliance.Control.ConsumptionX'
CONTROL_CONSUMPTIONH = 'Appliance.Control.ConsumptionH'

# Bulbs-only abilities
CONTROL_LIGHT = 'Appliance.Control.Light'
Expand Down
56 changes: 56 additions & 0 deletions tests/test_consumptionh.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import os
from ctypes import Union
from typing import Optional

from aiohttp import web
from aiohttp.test_utils import AioHTTPTestCase, unittest_run_loop

from meross_iot.controller.device import BaseDevice
from meross_iot.controller.mixins.consumption import ConsumptionHMixin
from meross_iot.manager import MerossManager
from meross_iot.model.enums import OnlineStatus
from tests import async_get_client

# Fix event loop for Windows env
if os.name == 'nt':
import asyncio
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
else:
import asyncio


class TestConsumptionH(AioHTTPTestCase):
async def get_application(self):
return web.Application()

async def setUpAsync(self):
# Wait some time before next test-burst
await asyncio.sleep(10)
self.meross_client, self.requires_logout = await async_get_client()

# Look for a device to be used for this test
self.meross_manager = MerossManager(http_client=self.meross_client)
await self.meross_manager.async_init()
devices = await self.meross_manager.async_device_discovery()
toggle_devices = self.meross_manager.find_devices(device_class=ConsumptionHMixin, online_status=OnlineStatus.ONLINE)

if len(toggle_devices) < 1:
self.test_device = None
else:
self.test_device = toggle_devices[0]

@unittest_run_loop
async def test_consumptionH_local_state(self):
if self.test_device is None:
self.skipTest("No ConsumptionH device has been found to run this test on.")
print(f"Testing device {self.test_device.name}")
r = await self.test_device.async_get_hourly_power_consumption()
self.assertGreater(len(r), 1)

async def tearDownAsync(self):
if self.requires_logout:
await self.meross_client.async_logout()
self.meross_manager.close()

# Give a change to asyncio clean everything up
await asyncio.sleep(1)