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

Check proto versio #11

Merged
merged 6 commits into from
Jun 27, 2023
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
19 changes: 17 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,31 @@ Regardless of chosen library we are required to have configured a Tuya Cloud pro
## Using pymoebot
> **_NOTE:_** The MoeBot needs to have been activated by adding it to the Tuya/SmartLife app first.

In it's most simplistic use, get an instance of `MoeBot` and query its status.
In it's most simplistic use, get an instance of `MoeBot`, poll for data and then show its attributes.

```python
from pymoebot import MoeBot

moebot = MoeBot('DEVICE_ID', 'IP', 'LOCAL_KEY')
moebot.poll()
print("Battery level: %s%" % moebot.battery)
```

See the [examples](https://github.com/Whytey/pymoebot/tree/main/examples) for full examples of usage.
Alternatively, register as a listener and then start the listening thread on your `MoeBot`.

```python
from pymoebot import MoeBot

moebot = MoeBot('DEVICE_ID', 'IP', 'LOCAL_KEY')
moebot.add_listener(lambda d: print("Got data: %s", d))
moebot.listen()

# Do some other stuff

moebot.unlisten()
```

See the [examples](https://github.com/Whytey/pymoebot/tree/main/examples) for further examples of usage.

# Communicating with the MoeBot

Expand Down
6 changes: 5 additions & 1 deletion examples/sniffer/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# sniffer.py

Dumps all relevant messages from the TuyaOpenMQ that are associated with a MoeBot.
Dumps all relevant messages from the `TuyaOpenMQ` that are associated with a MoeBot.

First, add you config to `default.ini` or create a new config file using the same format.

Call `python sniffer.ini`
7 changes: 6 additions & 1 deletion examples/sniffer/sniffer.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def read_config(filename=None):
if filename:
if os.path.exists(filename):
logging.debug("Reading extra config from '%s'", filename)
config.read('sniffer.ini')
config.read(filename)
else:
logging.warning("config file '%s' doesn't exist, ignoring.", filename)

Expand All @@ -38,6 +38,11 @@ def main():

moebot.add_listener(listener)

try:
moebot.listen()
except KeyboardInterrupt:
moebot.unlisten()


if __name__ == "__main__":
main()
41 changes: 36 additions & 5 deletions pymoebot/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,11 @@ def __init__(self, device_id: str, device_ip: str, local_key: str) -> None:
self.__key = local_key

self.__device = tinytuya.Device(self.__id, self.__ip, self.__key)
self.__device.set_version(3.3)
self.__device.set_socketPersistent(True)

self.__thread = threading.Thread(target=self.listen)
self.__thread.start()
self.__shutdown = threading.Event()
self.__tuya_version = self.__do_proto_check()
self.__device.set_version(self.__tuya_version)

self.__thread = threading.Thread(target=self.__loop)

self.__listeners = []

Expand All @@ -29,12 +28,25 @@ def __init__(self, device_id: str, device_ip: str, local_key: str) -> None:
self.__mow_in_rain = None
self.__mow_time = None
self.__work_mode = None
self.__online = False

def __do_proto_check(self) -> float:
versions = [3.4, 3.3]
for version in versions:
self.__device.set_version(version)
result = self.__device.status()
_log.debug("Set TUYA version to %r and got the following result: %r" % (version, result))
if self.__parse_payload(result):
return version

def __parse_payload(self, data) -> bool:
if data is None or 'Err' in data or 'dps' not in data:
_log.error("Error from device: %r" % data)
if 'Err' in data and data['Err'] == 905:
self.__online = False
return False

self.__online = True
_log.debug("Parsing data from device: %r" % data)
dps = data['dps']
if '6' in dps:
Expand All @@ -52,7 +64,17 @@ def __parse_payload(self, data) -> bool:

return True

def poll(self):
result = self.__device.status()
self.__parse_payload(result)

def listen(self):
self.__device.set_socketPersistent(True)

self.__thread.start()
self.__shutdown = threading.Event()

def __loop(self):
_log.debug(" > Send Request for Status < ")
payload = self.__device.generate_payload(tinytuya.DP_QUERY)
self.__device.send(payload)
Expand Down Expand Up @@ -82,11 +104,20 @@ def unlisten(self) -> None:
_log.debug("Unlistening to MoeBot")
self.__shutdown.set()
self.__thread.join()
self.__device.set_socketPersistent(False)

@property
def id(self) -> str:
return self.__id

@property
def online(self) -> str:
return self.__online

@property
def tuya_version(self) -> str:
return self.__tuya_version

@property
def mow_time(self) -> int:
return self.__mow_time
Expand Down
Empty file added test/__init__.py
Empty file.
26 changes: 23 additions & 3 deletions test/model_test.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,27 @@
import unittest
from unittest import mock

from pymoebot import MoeBotConnectionError, MoeBot


class TestMoeBot(unittest.TestCase):
def test_no_device(self):
self.assertRaises(MoeBotConnectionError, MoeBot("RANDOM", "127.0.0.1", "RANDOM_KEY_ABCDE"))

@mock.patch('tinytuya.Device.status')
def test_no_device(self, mock_status):
mock_status.return_value = {'Error': 'Network Error: Device Unreachable', 'Err': '905', 'Payload': None}

m = MoeBot("RANDOM", "127.0.0.1", "RANDOM_KEY_ABCDE")
self.assertFalse(m.online)
self.assertIsNone(m.state)

@mock.patch('tinytuya.Device.status')
def test_mock_device(self, mock_status):
mock_status.return_value = {
'dps': {'6': 100, '101': 'STANDBY', '102': 0, '103': 'MOWER_LEAN', '104': True, '105': 3, '106': 1111,
'114': 'AutoMode'}}

m = MoeBot("RANDOM", "127.0.0.1", "RANDOM_KEY_ABCDE")
self.assertFalse(m.online)
self.assertIsNone(m.state)
m.poll()
self.assertTrue(m.online)
self.assertIsNotNone(m.state)