Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update dependencies. #1

Merged
merged 1 commit into from Jul 16, 2019
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
@@ -1,7 +1,7 @@
{
"name": "meross-adapter",
"display_name": "Meross",
"version": "0.1.0",
"version": "0.2.0",
"description": "Meross smart plug adapter plugin for Mozilla WebThings Gateway",
"author": "Mozilla IoT",
"main": "main.py",
Expand Down
105 changes: 84 additions & 21 deletions pkg/meross_adapter.py
@@ -1,14 +1,17 @@
"""Meross adapter for Mozilla WebThings Gateway."""

from gateway_addon import Adapter, Database
from meross_iot.api import MerossHttpClient
from meross_iot.manager import MerossManager
from meross_iot.meross_event import MerossEventType
from meross_iot.cloud.devices.light_bulbs import GenericBulb
from meross_iot.cloud.devices.power_plugs import GenericPlug
from meross_iot.cloud.devices.door_openers import GenericGarageDoorOpener
import threading
import time

from .meross_device import MerossDevice


_TIMEOUT = 3


class MerossAdapter(Adapter):
"""Adapter for Meross smart home devices."""

Expand All @@ -24,55 +27,115 @@ def __init__(self, verbose=False):
'meross-adapter',
verbose=verbose)

self.client = None
self.manager = None
self.pairing = False

database = Database(self.package_name)
if database.open():
config = database.load_config()

if 'username' in config and len(config['username']) > 0 and \
'password' in config and len(config['password']) > 0:
self.client = MerossHttpClient(
email=config['username'],
password=config['password']
self.manager = MerossManager(
meross_email=config['username'],
meross_password=config['password']
)

self.manager.register_event_handler(self.event_handler)
self.manager.start()

database.close()

self.pairing = False
self.start_pairing(_TIMEOUT)
self.start_pairing()

def start_pairing(self, timeout):
def start_pairing(self, timeout=None):
"""
Start the pairing process.

timeout -- Timeout in seconds at which to quit pairing
"""
if self.client is None or self.pairing:
if self.manager is None or self.pairing:
return

self.pairing = True

for dev in self.client.list_supported_devices():
if not self.pairing:
break
for bulb in self.manager.get_devices_by_kind(GenericBulb):
pass

for plug in self.manager.get_devices_by_kind(GenericPlug):
if not plug.online:
continue

n_channels = len(dev.get_channels())
n_channels = len(plug.get_channels())

if n_channels > 1:
for channel in range(0, len(dev.get_channels())):
_id = 'meross-{}-{}'.format(dev.device_id(), channel)
for channel in range(0, len(plug.get_channels())):
_id = 'meross-{}-{}'.format(plug.uuid, channel)
if _id not in self.devices:
device = MerossDevice(self, _id, dev, channel=channel)
device = MerossDevice(self, _id, plug, channel=channel)
self.handle_device_added(device)
else:
_id = 'meross-{}'.format(dev.device_id())
_id = 'meross-{}'.format(plug.uuid)
if _id not in self.devices:
device = MerossDevice(self, _id, dev)
device = MerossDevice(self, _id, plug)
self.handle_device_added(device)

for opener in self.manager.get_devices_by_kind(GenericGarageDoorOpener): # noqa: E501
pass

self.pairing = False

def cancel_pairing(self):
"""Cancel the pairing process."""
self.pairing = False

def event_handler(self, obj):
"""Handle events from devices."""
if not hasattr(obj, 'device'):
return

_id = 'meross-{}'.format(obj.device.uuid)

devices = []
if _id in self.devices:
devices.append(self.devices[_id])
elif hasattr(obj, 'channel_id'):
_id = '{}-{}'.format(_id, obj.channel_id)

if _id in self.devices:
devices.append(self.devices[_id])
else:
return
else:
for k in self.devices.keys():
if k.startswith('{}-'.format(_id)):
devices.append(self.devices[k])

if len(devices) == 0:
# If the device wasn't found, but this is an online event, try to
# pair with it.
if obj.event_type == MerossEventType.DEVICE_ONLINE_STATUS and \
obj.status == 'online':

def delayed_pair(self):
time.sleep(5)
self.start_pairing()

t = threading.Thread(target=delayed_pair, args=(self,))
t.daemon = True
t.start()

return

if obj.event_type == MerossEventType.DEVICE_ONLINE_STATUS:
for device in devices:
device.connected_notify(obj.status == 'online')
elif obj.event_type == MerossEventType.DEVICE_SWITCH_STATUS:
for device in devices:
device.handle_toggle(obj.switch_state)
elif obj.event_type == MerossEventType.DEVICE_BULB_SWITCH_STATE:
pass
elif obj.event_type == MerossEventType.DEVICE_BULB_STATE:
pass
elif obj.event_type == MerossEventType.GARAGE_DOOR_STATUS:
pass
59 changes: 30 additions & 29 deletions pkg/meross_device.py
Expand Up @@ -27,8 +27,8 @@ def __init__(self, adapter, _id, meross_dev, channel=None):
self.type = 'onOffSwitch'

self.meross_dev = meross_dev
self.name = meross_dev._name
self.description = meross_dev._type
self.name = meross_dev.name
self.description = meross_dev.type
if not self.name:
self.name = self.description

Expand All @@ -46,7 +46,7 @@ def __init__(self, adapter, _id, meross_dev, channel=None):
'label': 'On/Off',
'type': 'boolean',
},
self.on)
False)

if self.meross_dev.supports_electricity_reading():
self._type.append('EnergyMonitor')
Expand All @@ -58,10 +58,10 @@ def __init__(self, adapter, _id, meross_dev, channel=None):
'@type': 'InstantaneousPowerProperty',
'label': 'Power',
'type': 'number',
'unit': 'Watt',
'unit': 'watt',
'readOnly': True,
},
self.power)
0)

self.properties['voltage'] = MerossProperty(
self,
Expand All @@ -73,7 +73,7 @@ def __init__(self, adapter, _id, meross_dev, channel=None):
'unit': 'volt',
'readOnly': True,
},
self.voltage)
0)

self.properties['current'] = MerossProperty(
self,
Expand All @@ -85,7 +85,7 @@ def __init__(self, adapter, _id, meross_dev, channel=None):
'unit': 'ampere',
'readOnly': True,
},
self.current)
0)

t = threading.Thread(target=self.poll)
t.daemon = True
Expand All @@ -94,27 +94,28 @@ def __init__(self, adapter, _id, meross_dev, channel=None):
def poll(self):
"""Poll the device for changes."""
while True:
time.sleep(_POLL_INTERVAL)

for prop in self.properties.values():
prop.update()

@property
def on(self):
"""Determine whether or not the device is on."""
return self.meross_dev.get_status(channel=self.channel)
if not self.meross_dev.online:
self.connected_notify(False)
continue

try:
on = self.meross_dev.get_status(channel=self.channel)
self.properties['on'].update(on)

if self.meross_dev.supports_electricity_reading():
e = self.meross_dev.get_electricity()
self.properties['power'].update(e['power'])
self.properties['voltage'].update(e['voltage'])
self.properties['current'].update(e['current'])

self.connected_notify(True)
except: # noqa: E722
# catching the exceptions from meross_iot just lead to more
# exceptions being thrown. cool.
self.connected_notify(False)

@property
def power(self):
"""Determine current power usage."""
return self.meross_dev.get_electricity()['electricity']['power']

@property
def voltage(self):
"""Determine current voltage."""
return self.meross_dev.get_electricity()['electricity']['voltage']
time.sleep(_POLL_INTERVAL)

@property
def current(self):
"""Determine current current."""
return self.meross_dev.get_electricity()['electricity']['current']
def handle_toggle(self, value):
"""Handle a switch toggle."""
self.properties['on'].update(value)
14 changes: 1 addition & 13 deletions pkg/meross_property.py
Expand Up @@ -39,20 +39,8 @@ def set_value(self, value):
self.set_cached_value(value)
self.device.notify_property_changed(self)

def update(self):
def update(self, value):
"""Update the current value, if necessary."""
value = None
if self.name == 'on':
value = self.device.on
elif self.name == 'power':
value = self.device.power
elif self.name == 'voltage':
value = self.device.voltage
elif self.name == 'current':
value = self.device.current
else:
return

if value != self.value:
self.set_cached_value(value)
self.device.notify_property_changed(self)
2 changes: 1 addition & 1 deletion requirements.txt
@@ -1 +1 @@
meross_iot==0.2.0.2
meross_iot==0.3.1.5