async def connect(self) -> None: if self._stopped: raise APIConnectionError("Connection is closed!") if self._connected: raise APIConnectionError("Already connected!") try: coro = resolve_ip_address(self._params.eventloop, self._params.address, self._params.port, self._params.zeroconf_instance) sockaddr = await asyncio.wait_for(coro, 30.0) except APIConnectionError as err: await self._on_error() raise err except asyncio.TimeoutError: await self._on_error() raise APIConnectionError("Timeout while resolving IP address") self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self._socket.setblocking(False) self._socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) _LOGGER.debug("%s: Connecting to %s:%s (%s)", self._params.address, self._params.address, self._params.port, sockaddr) try: coro = self._params.eventloop.sock_connect(self._socket, sockaddr) await asyncio.wait_for(coro, 30.0) except OSError as err: await self._on_error() raise APIConnectionError( "Error connecting to {}: {}".format(sockaddr, err)) except asyncio.TimeoutError: await self._on_error() raise APIConnectionError( "Timeout while connecting to {}".format(sockaddr)) _LOGGER.debug("%s: Opened socket for", self._params.address) self._socket_reader, self._socket_writer = await asyncio.open_connection(sock=self._socket) self._socket_connected = True self._params.eventloop.create_task(self.run_forever()) hello = pb.HelloRequest() hello.client_info = self._params.client_info try: resp = await self.send_message_await_response(hello, pb.HelloResponse) except APIConnectionError as err: await self._on_error() raise err _LOGGER.debug("%s: Successfully connected ('%s' API=%s.%s)", self._params.address, resp.server_info, resp.api_version_major, resp.api_version_minor) self._api_version = APIVersion( resp.api_version_major, resp.api_version_minor) if self._api_version.major > 2: _LOGGER.error("%s: Incompatible version %s! Closing connection", self._params.address, self._api_version.major) await self._on_error() raise APIConnectionError("Incompatible API version.") self._connected = True self._start_ping()
async def cover_command( self, key: int, position: Optional[float] = None, tilt: Optional[float] = None, stop: bool = False, ) -> None: self._check_authenticated() req = CoverCommandRequest() req.key = key apiv = cast(APIVersion, self.api_version) if apiv >= APIVersion(1, 1): if position is not None: req.has_position = True req.position = position if tilt is not None: req.has_tilt = True req.tilt = tilt if stop: req.stop = stop else: req.has_legacy_command = True if stop: req.legacy_command = LegacyCoverCommand.STOP elif position == 1.0: req.legacy_command = LegacyCoverCommand.OPEN else: req.legacy_command = LegacyCoverCommand.CLOSE assert self._connection is not None await self._connection.send_message(req)
async def execute_service(self, service: UserService, data: ExecuteServiceDataType) -> None: self._check_authenticated() req = ExecuteServiceRequest() req.key = service.key args = [] for arg_desc in service.args: arg = ExecuteServiceArgument() val = data[arg_desc.name] apiv = cast(APIVersion, self.api_version) int_type = "int_" if apiv >= APIVersion(1, 3) else "legacy_int" map_single = { UserServiceArgType.BOOL: "bool_", UserServiceArgType.INT: int_type, UserServiceArgType.FLOAT: "float_", UserServiceArgType.STRING: "string_", } map_array = { UserServiceArgType.BOOL_ARRAY: "bool_array", UserServiceArgType.INT_ARRAY: "int_array", UserServiceArgType.FLOAT_ARRAY: "float_array", UserServiceArgType.STRING_ARRAY: "string_array", } # pylint: disable=redefined-outer-name if arg_desc.type_ in map_array: attr = getattr(arg, map_array[arg_desc.type_]) attr.extend(val) else: setattr(arg, map_single[arg_desc.type_], val) args.append(arg) # pylint: disable=no-member req.args.extend(args) assert self._connection is not None await self._connection.send_message(req)
async def test_climate_command(auth_client, cmd, req): send = patch_send(auth_client) patch_api_version(auth_client, APIVersion(1, 5)) await auth_client.climate_command(**cmd) send.assert_called_once_with(ClimateCommandRequest(**req))
async def test_execute_service(auth_client): send = patch_send(auth_client) patch_api_version(auth_client, APIVersion(1, 3)) service = UserService( name="my_service", key=1, args=[ UserServiceArg(name="arg1", type=UserServiceArgType.BOOL), UserServiceArg(name="arg2", type=UserServiceArgType.INT), UserServiceArg(name="arg3", type=UserServiceArgType.FLOAT), UserServiceArg(name="arg4", type=UserServiceArgType.STRING), UserServiceArg(name="arg5", type=UserServiceArgType.BOOL_ARRAY), UserServiceArg(name="arg6", type=UserServiceArgType.INT_ARRAY), UserServiceArg(name="arg7", type=UserServiceArgType.FLOAT_ARRAY), UserServiceArg(name="arg8", type=UserServiceArgType.STRING_ARRAY), ], ) with pytest.raises(KeyError): await auth_client.execute_service(service, data={}) await auth_client.execute_service( service, data={ "arg1": False, "arg2": 42, "arg3": 99.0, "arg4": "asdf", "arg5": [False, True, False], "arg6": [42, 10, 9], "arg7": [0.0, -100.0], "arg8": [], }, ) send.assert_called_once_with( ExecuteServiceRequest( key=1, args=[ ExecuteServiceArgument(bool_=False), ExecuteServiceArgument(int_=42), ExecuteServiceArgument(float_=99.0), ExecuteServiceArgument(string_="asdf"), ExecuteServiceArgument(bool_array=[False, True, False]), ExecuteServiceArgument(int_array=[42, 10, 9]), ExecuteServiceArgument(float_array=[0.0, -100.0]), ExecuteServiceArgument(string_array=[]), ], )) send.reset_mock() patch_api_version(auth_client, APIVersion(1, 2)) service = UserService( name="my_service", key=2, args=[ UserServiceArg(name="arg1", type=UserServiceArgType.BOOL), UserServiceArg(name="arg2", type=UserServiceArgType.INT), ], ) # Test legacy_int await auth_client.execute_service( service, data={ "arg1": False, "arg2": 42, }, ) send.assert_called_once_with( ExecuteServiceRequest( key=2, args=[ ExecuteServiceArgument(bool_=False), ExecuteServiceArgument(legacy_int=42), ], )) send.reset_mock() # Test arg order await auth_client.execute_service( service, data={ "arg2": 42, "arg1": False, }, ) send.assert_called_once_with( ExecuteServiceRequest( key=2, args=[ ExecuteServiceArgument(bool_=False), ExecuteServiceArgument(legacy_int=42), ], )) send.reset_mock()
async def test_cover_command_legacy(auth_client, cmd, req): send = patch_send(auth_client) patch_api_version(auth_client, APIVersion(1, 0)) await auth_client.cover_command(**cmd) send.assert_called_once_with(CoverCommandRequest(**req))
def test_climate_state_preset_compat(state, version, out): assert state.preset_compat(APIVersion(*version)) == out
def test_climate_info_supported_presets_compat(state, version, out): assert state.supported_presets_compat(APIVersion(*version)) == out
def test_cover_state_legacy_state(state, version, out): assert state.is_closed(APIVersion(*version)) is out
def test_api_version_ord(): assert APIVersion(1, 0) == APIVersion(1, 0) assert APIVersion(1, 0) < APIVersion(1, 1) assert APIVersion(1, 1) <= APIVersion(1, 1) assert APIVersion(1, 0) < APIVersion(2, 0) assert not (APIVersion(2, 1) <= APIVersion(2, 0)) assert APIVersion(2, 1) > APIVersion(2, 0)