async def test_auth_refresh(aiohttp_server) -> None: r = Recorder() handler = NewDeviceHandler( r, [ { "name": "enterprises/project-id1/devices/device-id1", "type": "sdm.devices.types.device-type1", "traits": {}, "parentRelations": [], }, ], token="updated-token", ) async def auth_handler( request: aiohttp.web.Request) -> aiohttp.web.Response: return aiohttp.web.json_response({"token": "updated-token"}) app = aiohttp.web.Application() app.router.add_get("/enterprises/project-id1/devices", handler) app.router.add_get("/refresh-auth", auth_handler) server = await aiohttp_server(app) async with aiohttp.test_utils.TestClient(server) as client: api = google_nest_api.GoogleNestAPI(RefreshingAuth(client), PROJECT_ID) devices = await api.async_get_devices() assert len(devices) == 1 assert "enterprises/project-id1/devices/device-id1" == devices[0].name assert "sdm.devices.types.device-type1" == devices[0].type
async def test_get_devices(aiohttp_server) -> None: r = Recorder() handler = NewDeviceHandler( r, [ { "name": "enterprises/project-id1/devices/device-id1", "type": "sdm.devices.types.device-type1", "traits": {}, "parentRelations": [], }, { "name": "enterprises/project-id1/devices/device-id2", "type": "sdm.devices.types.device-type2", "traits": {}, "parentRelations": [], }, ], ) app = aiohttp.web.Application() app.router.add_get("/enterprises/project-id1/devices", handler) server = await aiohttp_server(app) async with aiohttp.test_utils.TestClient(server) as client: api = google_nest_api.GoogleNestAPI(FakeAuth(client), PROJECT_ID) devices = await api.async_get_devices() assert len(devices) == 2 assert "enterprises/project-id1/devices/device-id1" == devices[0].name assert "sdm.devices.types.device-type1" == devices[0].type assert "enterprises/project-id1/devices/device-id2" == devices[1].name assert "sdm.devices.types.device-type2" == devices[1].type
async def test_request(aiohttp_server) -> None: async def handler(request: aiohttp.web.Request) -> aiohttp.web.Response: assert request.headers['Authorization'] == 'Bearer some-token' return aiohttp.web.json_response({ 'devices': [ { 'name': 'enterprises/project-id/devices/device-id1', 'type': 'sdm.devices.types.device-type1', 'traits': {}, 'parentRelations': [], }, { 'name': 'enterprises/project-id/devices/device-id2', 'type': 'sdm.devices.types.device-type2', 'traits': {}, 'parentRelations': [], }, ] }) app = aiohttp.web.Application() app.router.add_get('/devices', handler) server = await aiohttp_server(app) async with aiohttp.test_utils.TestClient(server) as client: auth = FakeAuth(client) api = google_nest_api.GoogleNestAPI(auth) devices = await api.async_get_devices() assert len(devices) == 2 assert 'enterprises/project-id/devices/device-id1' == devices[0].name assert 'sdm.devices.types.device-type1' == devices[0].type assert 'enterprises/project-id/devices/device-id2' == devices[1].name assert 'sdm.devices.types.device-type2' == devices[1].type
async def test_api_get_error(aiohttp_server) -> None: # No server endpoint registered app = aiohttp.web.Application() server = await aiohttp_server(app) async with aiohttp.test_utils.TestClient(server) as client: api = google_nest_api.GoogleNestAPI(FakeAuth(client), PROJECT_ID) with pytest.raises(ApiException): await api.async_get_structures()
async def test_no_devices(aiohttp_server) -> None: r = Recorder() app = aiohttp.web.Application() app.router.add_get("/enterprises/project-id1/devices", NewHandler(r, [{}])) server = await aiohttp_server(app) async with aiohttp.test_utils.TestClient(server) as client: api = google_nest_api.GoogleNestAPI(FakeAuth(client), PROJECT_ID) devices = await api.async_get_devices() assert len(devices) == 0
async def test_missing_structures(aiohttp_server) -> None: r = Recorder() app = aiohttp.web.Application() app.router.add_get("/enterprises/project-id1/structures/abc", NewHandler(r, [{}])) server = await aiohttp_server(app) async with aiohttp.test_utils.TestClient(server) as client: api = google_nest_api.GoogleNestAPI(FakeAuth(client), PROJECT_ID) structure = await api.async_get_structure("abc") assert structure is None
async def test_camera_event_image(aiohttp_server) -> None: r = Recorder() handler = NewDeviceHandler( r, [{ "name": "enterprises/project-id1/devices/device-id1", "traits": { "sdm.devices.traits.CameraEventImage": {}, }, }], ) post_handler = NewRequestRecorder( r, [{ "results": { "url": "https://domain/sdm_event_snapshot/dGNUlTU2CjY5Y3VKaTZwR3o4Y", "token": "g.0.eventToken", }, }], ) app = aiohttp.web.Application() app.router.add_get("/enterprises/project-id1/devices", handler) app.router.add_post( "/enterprises/project-id1/devices/device-id1:executeCommand", post_handler) server = await aiohttp_server(app) async with aiohttp.test_utils.TestClient(server) as client: api = google_nest_api.GoogleNestAPI(FakeAuth(client), PROJECT_ID) devices = await api.async_get_devices() assert len(devices) == 1 device = devices[0] assert "enterprises/project-id1/devices/device-id1" == device.name trait = device.traits["sdm.devices.traits.CameraEventImage"] image = await trait.generate_image("some-eventId") expected_request = { "command": "sdm.devices.commands.CameraEventImage.GenerateImage", "params": { "eventId": "some-eventId" }, } assert expected_request == r.request assert ("https://domain/sdm_event_snapshot/dGNUlTU2CjY5Y3VKaTZwR3o4Y" == image.url) assert "g.0.eventToken" == image.token
async def test_auth_refresh_error(aiohttp_server) -> None: r = Recorder() async def auth_handler( request: aiohttp.web.Request) -> aiohttp.web.Response: return aiohttp.web.Response(status=401) app = aiohttp.web.Application() app.router.add_get("/enterprises/project-id1/devices", NewDeviceHandler(r, [])) app.router.add_get("/refresh-auth", auth_handler) server = await aiohttp_server(app) async with aiohttp.test_utils.TestClient(server) as client: api = google_nest_api.GoogleNestAPI(RefreshingAuth(client), PROJECT_ID) with pytest.raises(AuthException): await api.async_get_devices()
async def test_thermostat_eco_set_mode(aiohttp_server) -> None: r = Recorder() handler = NewDeviceHandler( r, [{ "name": "enterprises/project-id1/devices/device-id1", "traits": { "sdm.devices.traits.ThermostatEco": { "availableModes": ["MANUAL_ECO", "OFF"], "mode": "MANUAL_ECO", "heatCelsius": 20.0, "coolCelsius": 22.0, }, }, }], ) post_handler = NewRequestRecorder(r, []) app = aiohttp.web.Application() app.router.add_get("/enterprises/project-id1/devices", handler) app.router.add_post( "/enterprises/project-id1/devices/device-id1:executeCommand", post_handler) server = await aiohttp_server(app) async with aiohttp.test_utils.TestClient(server) as client: api = google_nest_api.GoogleNestAPI(FakeAuth(client), PROJECT_ID) devices = await api.async_get_devices() assert len(devices) == 1 device = devices[0] assert "enterprises/project-id1/devices/device-id1" == device.name trait = device.traits["sdm.devices.traits.ThermostatEco"] assert trait.mode == "MANUAL_ECO" await trait.set_mode("OFF") expected_request = { "command": "sdm.devices.commands.ThermostatEco.SetMode", "params": { "mode": "OFF" }, } assert expected_request == r.request
async def test_api_post_error(aiohttp_server) -> None: r = Recorder() handler = NewDeviceHandler( r, [{ "name": "enterprises/project-id1/devices/device-id1", "traits": { "sdm.devices.traits.ThermostatTemperatureSetpoint": { "heatCelsius": 23.0, "coolCelsius": 24.0, }, }, }], ) async def fail_handler( request: aiohttp.web.Request) -> aiohttp.web.Response: assert request.headers["Authorization"] == "Bearer %s" % FAKE_TOKEN return aiohttp.web.Response(status=502) app = aiohttp.web.Application() app.router.add_get("/enterprises/project-id1/devices", handler) app.router.add_post( "/enterprises/project-id1/devices/device-id1:executeCommand", fail_handler) server = await aiohttp_server(app) async with aiohttp.test_utils.TestClient(server) as client: api = google_nest_api.GoogleNestAPI(FakeAuth(client), PROJECT_ID) devices = await api.async_get_devices() assert len(devices) == 1 device = devices[0] assert "enterprises/project-id1/devices/device-id1" == device.name trait = device.traits[ "sdm.devices.traits.ThermostatTemperatureSetpoint"] assert trait.heat_celsius == 23.0 assert trait.cool_celsius == 24.0 with pytest.raises(ApiException): await trait.set_heat(25.0)
async def test_fan_set_timer(aiohttp_server) -> None: r = Recorder() handler = NewDeviceHandler( r, [{ "name": "enterprises/project-id1/devices/device-id1", "traits": { "sdm.devices.traits.Fan": { "timerMode": "OFF", }, }, }], ) post_handler = NewHandler(r, [{}]) app = aiohttp.web.Application() app.router.add_get("/enterprises/project-id1/devices", handler) app.router.add_post( "/enterprises/project-id1/devices/device-id1:executeCommand", post_handler) server = await aiohttp_server(app) async with aiohttp.test_utils.TestClient(server) as client: api = google_nest_api.GoogleNestAPI(FakeAuth(client), PROJECT_ID) devices = await api.async_get_devices() assert len(devices) == 1 device = devices[0] assert "enterprises/project-id1/devices/device-id1" == device.name trait = device.traits["sdm.devices.traits.Fan"] assert trait.timer_mode == "OFF" await trait.set_timer("ON", 3600) expected_request = { "command": "sdm.devices.commands.Fan.SetTimer", "params": { "timerMode": "ON", "duration": "3600s", }, } assert expected_request == r.request
async def test_get_structures(aiohttp_server) -> None: r = Recorder() handler = NewStructureHandler( r, [ { "name": "enterprises/project-id1/structures/structure-id1", "traits": { "sdm.structures.traits.Info": { "customName": "some-name1", } }, }, { "name": "enterprises/project-id1/structures/structure-id2", "traits": { "sdm.structures.traits.Info": { "customName": "some-name2", } }, }, ], ) app = aiohttp.web.Application() app.router.add_get("/enterprises/project-id1/structures", handler) server = await aiohttp_server(app) async with aiohttp.test_utils.TestClient(server) as client: api = google_nest_api.GoogleNestAPI(FakeAuth(client), PROJECT_ID) structures = await api.async_get_structures() assert len(structures) == 2 assert "enterprises/project-id1/structures/structure-id1" == structures[ 0].name assert "sdm.structures.traits.Info" in structures[0].traits assert "enterprises/project-id1/structures/structure-id2" == structures[ 1].name assert "sdm.structures.traits.Info" in structures[1].traits
async def test_camera_live_stream(aiohttp_server) -> None: r = Recorder() handler = NewDeviceHandler( r, [{ "name": "enterprises/project-id1/devices/device-id1", "traits": { "sdm.devices.traits.CameraLiveStream": {}, }, }], ) post_handler = NewRequestRecorder( r, [ { "results": { "streamUrls": { "rtsp_url": "rtsps://someurl.com/CjY5Y3VKaTZwR3o4Y19YbTVfMF...?auth=g.0.streamingToken" }, "streamExtensionToken": "CjY5Y3VKaTZwR3o4Y19YbTVfMF...", "streamToken": "g.0.streamingToken", "expiresAt": "2018-01-04T18:30:00.000Z", }, }, { "results": { "streamExtensionToken": "dGNUlTU2CjY5Y3VKaTZwR3o4Y1...", "streamToken": "g.1.newStreamingToken", "expiresAt": "2019-01-04T18:30:00.000Z", } }, { "results": { "streamExtensionToken": "last-token...", "streamToken": "g.2.newStreamingToken", "expiresAt": "2020-01-04T18:30:00.000Z", } }, {}, ], ) app = aiohttp.web.Application() app.router.add_get("/enterprises/project-id1/devices", handler) app.router.add_post( "/enterprises/project-id1/devices/device-id1:executeCommand", post_handler) server = await aiohttp_server(app) async with aiohttp.test_utils.TestClient(server) as client: api = google_nest_api.GoogleNestAPI(FakeAuth(client), PROJECT_ID) devices = await api.async_get_devices() assert len(devices) == 1 device = devices[0] assert "enterprises/project-id1/devices/device-id1" == device.name trait = device.traits["sdm.devices.traits.CameraLiveStream"] stream = await trait.generate_rtsp_stream() expected_request = { "command": "sdm.devices.commands.CameraLiveStream.GenerateRtspStream", "params": {}, } assert expected_request == r.request assert "g.0.streamingToken" == stream.stream_token assert (datetime.datetime( 2018, 1, 4, 18, 30, tzinfo=datetime.timezone.utc) == stream.expires_at) stream = await stream.extend_rtsp_stream() expected_request = { "command": "sdm.devices.commands.CameraLiveStream.ExtendRtspStream", "params": { "streamExtensionToken": "CjY5Y3VKaTZwR3o4Y19YbTVfMF...", }, } assert expected_request == r.request assert "g.1.newStreamingToken" == stream.stream_token assert (datetime.datetime( 2019, 1, 4, 18, 30, tzinfo=datetime.timezone.utc) == stream.expires_at) stream = await stream.extend_rtsp_stream() expected_request = { "command": "sdm.devices.commands.CameraLiveStream.ExtendRtspStream", "params": { "streamExtensionToken": "dGNUlTU2CjY5Y3VKaTZwR3o4Y1...", }, } assert expected_request == r.request assert "g.2.newStreamingToken" == stream.stream_token assert (datetime.datetime( 2020, 1, 4, 18, 30, tzinfo=datetime.timezone.utc) == stream.expires_at) await stream.stop_rtsp_stream() expected_request = { "command": "sdm.devices.commands.CameraLiveStream.StopRtspStream", "params": { "streamExtensionToken": "last-token...", }, } assert expected_request == r.request
async def test_thermostat_temperature_set_point(aiohttp_server) -> None: r = Recorder() handler = NewDeviceHandler( r, [{ "name": "enterprises/project-id1/devices/device-id1", "traits": { "sdm.devices.traits.ThermostatTemperatureSetpoint": { "heatCelsius": 23.0, "coolCelsius": 24.0, }, }, }], ) post_handler = NewRequestRecorder(r, []) app = aiohttp.web.Application() app.router.add_get("/enterprises/project-id1/devices", handler) app.router.add_post( "/enterprises/project-id1/devices/device-id1:executeCommand", post_handler) server = await aiohttp_server(app) async with aiohttp.test_utils.TestClient(server) as client: api = google_nest_api.GoogleNestAPI(FakeAuth(client), PROJECT_ID) devices = await api.async_get_devices() assert len(devices) == 1 device = devices[0] assert "enterprises/project-id1/devices/device-id1" == device.name trait = device.traits[ "sdm.devices.traits.ThermostatTemperatureSetpoint"] assert trait.heat_celsius == 23.0 assert trait.cool_celsius == 24.0 await trait.set_heat(25.0) expected_request = { "command": "sdm.devices.commands.ThermostatTemperatureSetpoint.SetHeat", "params": { "heatCelsius": 25.0 }, } assert expected_request == r.request await trait.set_cool(26.0) expected_request = { "command": "sdm.devices.commands.ThermostatTemperatureSetpoint.SetCool", "params": { "coolCelsius": 26.0 }, } assert expected_request == r.request await trait.set_range(27.0, 28.0) expected_request = { "command": "sdm.devices.commands.ThermostatTemperatureSetpoint.SetRange", "params": { "heatCelsius": 27.0, "coolCelsius": 28.0, }, } assert expected_request == r.request