async def test_no_data(event_loop, aresponses): """Test error is raised when info data is missing from API call.""" # Handle to run asserts on request in aresponses.add( "example.com:8081", "/zeroconf/info", "POST", aresponses.Response( status=200, headers={"Content-Type": "application/json"}, text='{"seq": 6,"error": 0}', ), ) aresponses.add( "example.com:8081", "/zeroconf/signal_strength", "POST", aresponses.Response( status=200, headers={"Content-Type": "application/json"}, text='{"seq": 26, "error": 0, "data": {"signalStrength": -100}}', ), ) async with aiohttp.ClientSession(loop=event_loop) as session: diy = SonoffDIY( "example.com", device_id="100090ab1a", session=session, loop=event_loop, ) with pytest.raises(SonoffDIYError): await diy.update_info()
async def test_ota_flash(event_loop, aresponses): """Test flashing Sonoff DIY device.""" # Handle to run asserts on request in async def response_handler(request): data = await request.json() assert data == { "deviceid": "100090ab1a", "data": { "downloadUrl": "https://frenck.dev", "sha256sum": "1d869a86cd206fc09aaa", }, } return aresponses.Response( status=200, headers={"Content-Type": "application/json"}, text='{"seq": 1, "error": 0}', ) aresponses.add("example.com:8081", "/zeroconf/ota_flash", "POST", response_handler) async with aiohttp.ClientSession(loop=event_loop) as session: diy = SonoffDIY( "example.com", device_id="100090ab1a", session=session, loop=event_loop, ) await diy.ota_flash( "https://frenck.dev", "1d869a86cd206fc09aaa", )
async def test_invalid_content_type(event_loop, aresponses): """Test request timeout from Sonoff DIY device.""" aresponses.add( "example.com:8081", "/", "POST", aresponses.Response( status=200, headers={"Content-Type": "other/content"}, text='{"seq": 26, "error": 0, "data": {"test": "ok"}}', ), ) aresponses.add( "example.com:8081", "/", "POST", aresponses.Response( status=200, text='{"seq": 26, "error": 0, "data": {"test": "ok"}}', ), ) async with aiohttp.ClientSession(loop=event_loop) as session: diy = SonoffDIY("example.com", session=session, loop=event_loop) with pytest.raises(SonoffDIYError): await diy._request("/") with pytest.raises(SonoffDIYError): await diy._request("/")
async def test_power_on_state_invalid(event_loop): """Test setting invalid power on state.""" async with aiohttp.ClientSession(loop=event_loop) as session: diy = SonoffDIY( "example.com", device_id="100090ab1a", session=session, loop=event_loop, ) with pytest.raises(SonoffDIYError): await diy.power_on_state("frenck")
async def main(loop): """Show example on controlling your Sonoff DIY device.""" async with SonoffDIY("10.10.10.197", device_id="100090bc7b", loop=loop) as diy: info = await diy.update_info() print(info) if not info.on: await diy.turn_on() else: await diy.turn_off()
async def test_info_update(event_loop, aresponses): """Test getting Sonoff DIY device information and states.""" # Handle to run asserts on request in async def response_handler(request): data = await request.json() assert data == {"deviceid": "100090ab1a", "data": {}} return aresponses.Response( status=200, headers={"Content-Type": "application/json"}, text=str( '{"seq": 6,"error": 0,"data": ' '"{\\"switch\\":\\"on\\",\\"startup\\":\\"stay\\",' '\\"pulse\\":\\"off\\",\\"pulseWidth\\":1500,' '\\"ssid\\":\\"frenck\\",\\"otaUnlock\\":true}"}', ), ) aresponses.add("example.com:8081", "/zeroconf/info", "POST", response_handler) aresponses.add( "example.com:8081", "/zeroconf/signal_strength", "POST", aresponses.Response( status=200, headers={"Content-Type": "application/json"}, text='{"seq": 26, "error": 0, "data": {"signalStrength": -48}}', ), ) async with aiohttp.ClientSession(loop=event_loop) as session: diy = SonoffDIY( "example.com", device_id="100090ab1a", session=session, loop=event_loop, ) info = await diy.update_info() assert info assert info.on assert info.power_on_state == STATE_RESTORE assert not info.pulse assert info.pulse_width == 1500 assert info.ssid == "frenck" assert info.ota_unlock assert info.signal_strength == -48 assert info.signal_strength_percentage == 100
async def test_internal_eventloop(aresponses): """Test internal event loop creation is handled correctly.""" aresponses.add( "example.com:8081", "/", "POST", aresponses.Response( status=200, headers={"Content-Type": "application/json"}, text='{"seq": 26, "error": 0, "data": {"test": "ok"}}', ), ) async with SonoffDIY("example.com") as diy: response = await diy._request("/") assert response["test"] == "ok"
async def test_encoded_json_request(event_loop, aresponses): """Test JSON response is handled correctly.""" aresponses.add( "example.com:8081", "/", "POST", aresponses.Response( status=200, headers={"Content-Type": "application/json"}, text='{"seq": 26, "error": 0, "data": "{\\"test\\": \\"ok\\"}"}', ), ) async with aiohttp.ClientSession(loop=event_loop) as session: diy = SonoffDIY("example.com", session=session, loop=event_loop) response = await diy._request("/") assert response["test"] == "ok"
async def test_error_response(event_loop, aresponses): """Test error response from Sonoff DIY device.""" aresponses.add( "example.com:8081", "/", "POST", aresponses.Response( status=200, headers={"Content-Type": "application/json"}, text='{"seq": 26, "error": 422}', ), ) async with aiohttp.ClientSession(loop=event_loop) as session: diy = SonoffDIY("example.com", session=session, loop=event_loop) with pytest.raises(SonoffDIYError): await diy._request("/")
async def test_pulse_width(event_loop, aresponses): """Test setting Sonoff DIY device pulse width.""" # Handle to run asserts on request in async def response_handler(request): data = await request.json() assert data == { "deviceid": "100090ab1a", "data": {"pulse": STATE_ON, "pulse_width": 1000}, } return aresponses.Response( status=200, headers={"Content-Type": "application/json"}, text='{"seq": 1, "error": 0}', ) aresponses.add( "example.com:8081", "/zeroconf/info", "POST", aresponses.Response( status=200, headers={"Content-Type": "application/json"}, text='{"seq": 26, "error": 0, "data": {"pulse": "on"}}', ), ) aresponses.add( "example.com:8081", "/zeroconf/signal_strength", "POST", aresponses.Response( status=200, headers={"Content-Type": "application/json"}, text='{"seq": 26, "error": 0, "data": {"signalStrength": -48}}', ), ) aresponses.add("example.com:8081", "/zeroconf/pulse", "POST", response_handler) async with aiohttp.ClientSession(loop=event_loop) as session: diy = SonoffDIY( "example.com", device_id="100090ab1a", session=session, loop=event_loop, ) await diy.pulse_width(1000)
async def test_request_user_agent(event_loop, aresponses): """Test client sending correct user agent headers.""" # Handle to run asserts on request in async def response_handler(request): assert request.headers[ "User-Agent"] == f"PythonSonoffDIY/{__version__}" return aresponses.Response( status=200, headers={"Content-Type": "application/json"}, text='{"seq": 1, "error": 0}', ) aresponses.add("example.com:8081", "/", "POST", response_handler) async with aiohttp.ClientSession(loop=event_loop) as session: diy = SonoffDIY("example.com", session=session, loop=event_loop) await diy._request("/")
async def test_request_port(event_loop, aresponses): """Test Sonoff DIY device running on non-standard port.""" aresponses.add( "example.com:8888", "/", "POST", aresponses.Response( status=200, headers={"Content-Type": "application/json"}, text='{"seq": 26, "error": 0, "data": {"test": "ok"}}', ), ) async with aiohttp.ClientSession(loop=event_loop) as session: diy = SonoffDIY("example.com", port=8888, session=session, loop=event_loop) response = await diy._request("/") assert response["test"] == "ok"
async def test_switch_on(event_loop, aresponses): """Test turning on Sonoff DIY device switch.""" # Handle to run asserts on request in async def response_handler(request): data = await request.json() assert data == {"deviceid": "100090ab1a", "data": {"switch": STATE_ON}} return aresponses.Response( status=200, headers={"Content-Type": "application/json"}, text='{"seq": 1, "error": 0}', ) aresponses.add("example.com:8081", "/zeroconf/switch", "POST", response_handler) async with aiohttp.ClientSession(loop=event_loop) as session: diy = SonoffDIY( "example.com", device_id="100090ab1a", session=session, loop=event_loop, ) await diy.turn_on()
async def test_timeout(event_loop, aresponses): """Test request timeout from Sonoff DIY device.""" # Faking a timeout by sleeping async def response_handler(_): await asyncio.sleep(2) return aresponses.Response( status=200, headers={"Content-Type": "application/json"}, text='{"seq": 1, "error": 0}', ) aresponses.add("example.com:8081", "/", "POST", response_handler) async with aiohttp.ClientSession(loop=event_loop) as session: diy = SonoffDIY("example.com", session=session, loop=event_loop, request_timeout=1) with pytest.raises(SonoffDIYConnectionError): assert await diy._request("/")
async def test_http_error(event_loop, aresponses): """Test HTTP error response handling.""" aresponses.add( "example.com:8081", "/", "POST", aresponses.Response(text="OMG PUPPIES!", status=404), ) aresponses.add( "example.com:8081", "/", "POST", aresponses.Response(text="OMG PUPPIES!", status=500), ) async with aiohttp.ClientSession(loop=event_loop) as session: diy = SonoffDIY("example.com", session=session, loop=event_loop) with pytest.raises(SonoffDIYError): assert await diy._request("/") with pytest.raises(SonoffDIYError): assert await diy._request("/")
async def test_signal_strength(event_loop, aresponses): """Test retreiving Sonoff DIY device WiFi signal strength.""" # Handle to run asserts on request in async def response_handler(request): data = await request.json() assert data == {"deviceid": "100090ab1a", "data": {}} return aresponses.Response( status=200, headers={"Content-Type": "application/json"}, text='{"seq": 6,"error": 0,"data": {}}', ) aresponses.add("example.com:8081", "/zeroconf/info", "POST", response_handler) aresponses.add( "example.com:8081", "/zeroconf/signal_strength", "POST", aresponses.Response( status=200, headers={"Content-Type": "application/json"}, text='{"seq": 26, "error": 0, "data": {"signalStrength": -60}}', ), ) async with aiohttp.ClientSession(loop=event_loop) as session: diy = SonoffDIY( "example.com", device_id="100090ab1a", session=session, loop=event_loop, ) info = await diy.update_info() assert info assert info.signal_strength == -60 assert info.signal_strength_percentage == 80
async def switchMain(loop, ip, deviceID, state): tryAgain = True count = 4 """Show example on controlling your Sonoff DIY device.""" while tryAgain: try: # async with SonoffDIY("192.168.10.169", device_id="1000c8c4b5", loop=loop) as diy: async with SonoffDIY(ip, device_id=deviceID, loop=loop) as diy: info = await diy.update_info() if state == "on": await diy.turn_on() else: await diy.turn_off() tryAgain = False except: count -= 1 tryAgain = True if count < 0: print("Tried and failed.")
async def test_wifi(event_loop, aresponses): """Test setting Sonoff DIY device WiFi settings.""" # Handle to run asserts on request in async def response_handler(request): data = await request.json() assert data == { "deviceid": "100090ab1a", "data": {"ssid": "frenck", "password": "******"}, } return aresponses.Response( status=200, headers={"Content-Type": "application/json"}, text='{"seq": 1, "error": 0}', ) aresponses.add("example.com:8081", "/zeroconf/wifi", "POST", response_handler) async with aiohttp.ClientSession(loop=event_loop) as session: diy = SonoffDIY( "example.com", device_id="100090ab1a", session=session, loop=event_loop, ) await diy.wifi("frenck", "choochoo")
async def test_info_none(event_loop, aresponses): """Test info data is None when communication has occured.""" # Handle to run asserts on request in aresponses.add( "example.com:8081", "/zeroconf/info", "POST", aresponses.Response( status=500, headers={"Content-Type": "application/json"}, text="Invalid response", ), ) async with aiohttp.ClientSession(loop=event_loop) as session: diy = SonoffDIY( "example.com", device_id="100090ab1a", session=session, loop=event_loop, ) with pytest.raises(SonoffDIYError): await diy.update_info() assert diy.info is None
async def infoMain(loop): tryAgain = True count = 4 configFile = "/etc/HomeAutomation/config.json" name = "" verbose = False try: opts, args = getopt.getopt(sys.argv[1:], "c:hvn:", ["config=", "help", "name="]) except getopt.GetoptError as err: print(err) usage() sys.exit(2) for o, a in opts: if o in ("-v", "--verbose"): verbose = True elif o in ("-c", "--config"): configFile = a elif o in ("-h", "--help"): usage() sys.exit(0) elif o in ("-n", "--name"): name = a sonoff = readConfig(configFile) ip = "" deviceID = "" if sonoff.get(name): ip = sonoff["test"]["ip"] deviceID = sonoff["test"]["device_id"] if verbose: print("Name :" + name) print("\tIP :" + ip) print("\tDevice ID :" + deviceID) else: print(name + " not found") tryAgain = False while tryAgain: try: async with SonoffDIY(ip, device_id=deviceID, loop=loop) as diy: info = await diy.update_info_json() tmp = json.loads(info) if verbose: print(tmp) print(tmp['switch'].upper()) tryAgain = False except Exception as ex: template = "An exception of type {0} occurred. Arguments:\n{1!r}" message = template.format(type(ex).__name__, ex.args) print(message) tryAgain = False except SonoffDIYConnectionError: count -= 1 tryAgain = True if count < 0: print("Tried and failed.") sys.exit(1) else: sleep(0.2)