diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index b46aed29c..aa2472094 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -12,12 +12,14 @@ on: jobs: generate-docs: if: github.repository_owner == 'viamrobotics' - runs-on: [self-hosted, x64] - container: - image: ghcr.io/viamrobotics/canon:amd64 + runs-on: ubuntu-latest steps: - - name: Checkout Push/Workflow Dispatch - uses: actions/checkout@v3 + - uses: actions/checkout@v4 + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" - name: Install Poetry uses: snok/install-poetry@v1 diff --git a/.github/workflows/license_finder.yml b/.github/workflows/license_finder.yml index 2a5190c94..3ee9fae7e 100644 --- a/.github/workflows/license_finder.yml +++ b/.github/workflows/license_finder.yml @@ -10,17 +10,13 @@ jobs: license_finder: if: github.repository_owner == 'viamrobotics' name: Audit 3rd-Party Licenses - runs-on: [x64, qemu-host] + runs-on: ubuntu-latest container: image: ghcr.io/viamrobotics/canon:amd64-cache - options: --platform linux/amd64 timeout-minutes: 30 steps: - - name: Check out code in rdk directory - uses: actions/checkout@v2 - with: - fetch-depth: 2 + - uses: actions/checkout@v4 - name: Install Poetry uses: snok/install-poetry@v1 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index de4f8a84f..c4daafe96 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -7,9 +7,7 @@ on: jobs: publish: if: github.repository_owner == 'viamrobotics' - runs-on: [self-hosted, x64] - container: - image: ghcr.io/viamrobotics/canon:amd64 + runs-on: ubuntu-latest steps: - name: Download Release diff --git a/.github/workflows/release-candidate.yml b/.github/workflows/release-candidate.yml index c9692d331..074f6cf50 100644 --- a/.github/workflows/release-candidate.yml +++ b/.github/workflows/release-candidate.yml @@ -17,27 +17,18 @@ on: jobs: prepare: if: github.repository_owner == 'viamrobotics' - runs-on: [self-hosted, x64] - container: - image: ghcr.io/viamrobotics/canon:amd64 + runs-on: ubuntu-latest outputs: rc_version: ${{ steps.bump_version.outputs.rc_version }} version: ${{ steps.bump_version.outputs.version }} steps: - - name: Output Event - run: echo "${{ toJSON(github.event) }}" - - - name: Install GH CLI - run: | - type -p curl >/dev/null || (sudo apt update && sudo apt install curl -y) - curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg \ - && sudo chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg \ - && echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null \ - && sudo apt update \ - && sudo apt install gh -y - - name: Checkout Code - uses: actions/checkout@v3 + uses: actions/checkout@v4 + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" - name: Install Poetry uses: snok/install-poetry@v1 @@ -97,9 +88,7 @@ jobs: build: needs: prepare if: github.repository_owner == 'viamrobotics' - runs-on: [self-hosted, x64] - container: - image: ghcr.io/viamrobotics/canon:amd64 + runs-on: ubuntu-latest strategy: matrix: include: @@ -126,10 +115,15 @@ jobs: whl: linux_armv7l steps: - name: Checkout Code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: rc-${{ needs.prepare.outputs.version }} + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + - name: Install Poetry uses: snok/install-poetry@v1 @@ -158,9 +152,7 @@ jobs: release: needs: [prepare, build] if: github.repository_owner == 'viamrobotics' - runs-on: [self-hosted, x64] - container: - image: ghcr.io/viamrobotics/canon:amd64 + runs-on: ubuntu-latest steps: - uses: actions/download-artifact@v3 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c2300af6c..36eb2c38b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -92,7 +92,7 @@ jobs: whl: linux_armv7l steps: - name: Checkout Code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: rc-${{ needs.prepare.outputs.version }} diff --git a/examples/server/v1/components.py b/examples/server/v1/components.py index 149d982b5..4c9f64339 100644 --- a/examples/server/v1/components.py +++ b/examples/server/v1/components.py @@ -19,7 +19,7 @@ from viam.components.arm import Arm from viam.components.audio_input import AudioInput from viam.components.base import Base -from viam.components.board import Board +from viam.components.board import Board, TickStream from viam.components.camera import Camera from viam.components.encoder import Encoder from viam.components.gantry import Gantry @@ -31,7 +31,7 @@ from viam.components.sensor import Sensor from viam.components.servo import Servo from viam.errors import ResourceNotFoundError -from viam.media import MediaStreamWithIterator +from viam.streams import StreamWithIterator from viam.media.audio import Audio, AudioStream from viam.media.video import NamedImage from viam.operations import run_with_operation @@ -155,7 +155,7 @@ async def read() -> AsyncIterator[Audio]: await asyncio.sleep(self.latency.total_seconds()) - return MediaStreamWithIterator(read()) + return StreamWithIterator(read()) async def get_properties(self) -> AudioInput.Properties: return AudioInput.Properties( @@ -314,6 +314,11 @@ async def set_power_mode(self, **kwargs): async def write_analog(self, pin: str, value: int, *, timeout: Optional[float] = None, **kwargs): raise NotImplementedError() + async def stream_ticks( + self, interrupts: List[Board.DigitalInterrupt], *, timeout: Optional[float] = None, **kwargs + ) -> TickStream: + raise NotImplementedError() + async def get_geometries(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> List[Geometry]: return GEOMETRIES diff --git a/pyproject.toml b/pyproject.toml index f7d188d2c..4dbe2cb9a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "viam-sdk" -version = "0.17.0" +version = "0.18.0" description = "Viam Robotics Python SDK" authors = [ "Naveed " ] license = "Apache-2.0" diff --git a/src/viam/components/audio_input/audio_input.py b/src/viam/components/audio_input/audio_input.py index e47a65bbe..ea13e6fc2 100644 --- a/src/viam/components/audio_input/audio_input.py +++ b/src/viam/components/audio_input/audio_input.py @@ -6,7 +6,7 @@ from google.protobuf.duration_pb2 import Duration from typing_extensions import Self -from viam.media import MediaSource +from viam.streams import StreamSource from viam.media.audio import Audio, AudioStream from viam.proto.component.audioinput import PropertiesResponse from viam.resource.types import RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT, Subtype @@ -14,7 +14,7 @@ from ..component_base import ComponentBase -class AudioInput(ComponentBase, MediaSource[Audio]): +class AudioInput(ComponentBase, StreamSource[Audio]): """AudioInput represents a component that can capture audio. This acts as an abstract base class for any drivers representing specific @@ -67,7 +67,7 @@ async def stream(self, *, timeout: Optional[float] = None, **kwargs) -> AudioStr """Stream audio samples from the audio input of the underlying robot Returns: - MediaStream[Audio]: The stream of audio chunks + Stream[Audio]: The stream of audio chunks """ ... diff --git a/src/viam/components/audio_input/client.py b/src/viam/components/audio_input/client.py index 6e654e374..2609dd62d 100644 --- a/src/viam/components/audio_input/client.py +++ b/src/viam/components/audio_input/client.py @@ -2,7 +2,7 @@ from grpclib.client import Channel -from viam.media import MediaStream, MediaStreamWithIterator +from viam.streams import Stream, StreamWithIterator from viam.media.audio import Audio from viam.proto.common import DoCommandRequest, DoCommandResponse, Geometry from viam.proto.component.audioinput import ( @@ -29,7 +29,7 @@ def __init__(self, name: str, channel: Channel): self.client = AudioInputServiceStub(channel) super().__init__(name) - async def stream(self, *, timeout: Optional[float] = None, **__) -> MediaStream[Audio]: + async def stream(self, *, timeout: Optional[float] = None, **__) -> Stream[Audio]: async def read() -> AsyncIterator[Audio]: async with self.client.Chunks.open(timeout=timeout) as chunks_stream: await chunks_stream.send_message( @@ -50,7 +50,7 @@ async def read() -> AsyncIterator[Audio]: audio = Audio(info=info, chunk=response.chunk) yield audio - return MediaStreamWithIterator(read()) + return StreamWithIterator(read()) async def get_properties(self, *, timeout: Optional[float] = None, **__) -> AudioInput.Properties: request = PropertiesRequest(name=self.name) diff --git a/src/viam/components/board/__init__.py b/src/viam/components/board/__init__.py index ca452468a..e2f6f7e20 100644 --- a/src/viam/components/board/__init__.py +++ b/src/viam/components/board/__init__.py @@ -5,12 +5,12 @@ from viam.resource.registry import Registry, ResourceRegistration from viam.utils import message_to_struct -from .board import Board +from .board import Board, Tick, TickStream from .client import BoardClient from .service import BoardRPCService __all__ = [ - "Board", + "Board" ] diff --git a/src/viam/components/board/board.py b/src/viam/components/board/board.py index 9673df581..955226ced 100644 --- a/src/viam/components/board/board.py +++ b/src/viam/components/board/board.py @@ -2,11 +2,15 @@ from datetime import timedelta from typing import Any, Dict, Final, List, Optional -from viam.proto.component.board import PowerMode +from viam.proto.component.board import PowerMode, StreamTicksResponse from viam.resource.types import RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT, Subtype +from viam.streams import Stream from ..component_base import ComponentBase +Tick = StreamTicksResponse +TickStream = Stream[Tick] + class Board(ComponentBase): """ @@ -372,3 +376,28 @@ async def write_analog(self, pin: str, value: int, *, timeout: Optional[float] = value (int): value to write. """ ... + + @abc.abstractmethod + async def stream_ticks( + self, interrupts: List[DigitalInterrupt], *, timeout: Optional[float] = None, **kwargs + ) -> TickStream: + """ + Stream digital interrupt ticks. + + :: + + + my_board = Board.from_robot(robot=robot, name="my_board") + di8 = await my_board.digital_interrupt_by_name(name="8")) + di11 = await my_board.digital_interrupt_by_name(name="11")) + + Stream ticks from pins 8 and 11. + ticks = my_board.stream_ticks([di8, di11]) + + Args: + interrupts (List[DigitalInterrupt]) : list of digital interrupts to recieve ticks from. + + Returns: + TickStream: stream of ticks. + """ + ... diff --git a/src/viam/components/board/client.py b/src/viam/components/board/client.py index 06431e768..da1c8ca9c 100644 --- a/src/viam/components/board/client.py +++ b/src/viam/components/board/client.py @@ -2,8 +2,9 @@ from typing import Any, Dict, List, Mapping, Optional from google.protobuf.duration_pb2 import Duration -from grpclib.client import Channel +from grpclib.client import Channel, Stream as ClientStream +from viam.logging import getLogger from viam.proto.common import DoCommandRequest, DoCommandResponse, Geometry from viam.proto.component.board import ( BoardServiceStub, @@ -22,12 +23,17 @@ SetPowerModeRequest, SetPWMFrequencyRequest, SetPWMRequest, + StreamTicksRequest, + StreamTicksResponse, WriteAnalogRequest, ) +from viam.streams import StreamWithIterator from viam.resource.rpc_client_base import ReconfigurableResourceRPCClientBase from viam.utils import ValueTypes, dict_to_struct, get_geometries, struct_to_dict -from . import Board +from .board import Board, TickStream + +LOGGER = getLogger(__name__) class AnalogReaderClient(Board.AnalogReader): @@ -224,3 +230,29 @@ async def write_analog( extra = {} request = WriteAnalogRequest(name=self.name, pin=pin, value=value, extra=dict_to_struct(extra)) await self.client.WriteAnalog(request, timeout=timeout) + + async def stream_ticks( + self, + interrupts: List[Board.DigitalInterrupt], + *, + extra: Optional[Dict[str, Any]] = None, + **__, + ) -> TickStream: + if extra is None: + extra = {} + names = [] + for di in interrupts: + names.append(di.name) + request = StreamTicksRequest(name=self.name, pin_names=names, extra=dict_to_struct(extra)) + + async def read(): + tick_stream: ClientStream[StreamTicksRequest, StreamTicksResponse] + async with self.client.StreamTicks.open() as tick_stream: + try: + await tick_stream.send_message(request, end=True) + async for tick in tick_stream: + yield tick + except Exception as e: + raise (e) + + return StreamWithIterator(read()) diff --git a/src/viam/components/board/service.py b/src/viam/components/board/service.py index f236d81c8..afc2024bc 100644 --- a/src/viam/components/board/service.py +++ b/src/viam/components/board/service.py @@ -1,6 +1,8 @@ from grpclib.server import Stream +from h2.exceptions import StreamClosedError -from viam.errors import MethodNotImplementedError, ResourceNotFoundError +from viam.errors import ResourceNotFoundError +from viam.logging import getLogger from viam.proto.common import DoCommandRequest, DoCommandResponse, GetGeometriesRequest, GetGeometriesResponse from viam.proto.component.board import ( BoardServiceBase, @@ -32,6 +34,8 @@ from .board import Board +LOGGER = getLogger(__name__) + class BoardRPCService(BoardServiceBase, ResourceRPCServiceBase[Board]): """ @@ -196,4 +200,21 @@ async def GetGeometries(self, stream: Stream[GetGeometriesRequest, GetGeometries await stream.send_message(response) async def StreamTicks(self, stream: Stream[StreamTicksRequest, StreamTicksResponse]) -> None: - raise MethodNotImplementedError("StreamTicks").grpc_error + request = await stream.recv_message() + assert request is not None + name = request.name + board = self.get_resource(name) + + dis = [] + for name in request.pin_names: + dis.append(await board.digital_interrupt_by_name(name)) + + tick_stream = await board.stream_ticks(interrupts=dis, metadata=stream.metadata) + async for tick in tick_stream: + try: + await stream.send_message(tick) + except StreamClosedError: + return + except Exception as e: + LOGGER.error(e) + return diff --git a/src/viam/logging.py b/src/viam/logging.py index 329649d6a..5d33aca62 100644 --- a/src/viam/logging.py +++ b/src/viam/logging.py @@ -39,8 +39,6 @@ def handle_task_result(self, task: asyncio.Task): _ = task.result() except (asyncio.CancelledError, asyncio.InvalidStateError, StreamTerminatedError): pass - except Exception: - self._logger.exception("Exception raised by task = %r", task) def emit(self, record: logging.LogRecord): assert isinstance(record, logging.LogRecord) @@ -51,9 +49,18 @@ def emit(self, record: logging.LogRecord): try: assert self._parent is not None - asyncio.create_task( - self._parent.log(name, record.levelname, time, message, stack), name=f"{viam._TASK_PREFIX}-LOG-{record.created}" - ).add_done_callback(self.handle_task_result) + try: + loop = asyncio.get_event_loop() + loop.create_task( + self._parent.log(name, record.levelname, time, message, stack), name=f"{viam._TASK_PREFIX}-LOG-{record.created}" + ).add_done_callback(self.handle_task_result) + except RuntimeError: + # If the log is coming from a thread that doesn't have an event loop, create and set a new one. + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + loop.create_task( + self._parent.log(name, record.levelname, time, message, stack), name=f"{viam._TASK_PREFIX}-LOG-{record.created}" + ).add_done_callback(self.handle_task_result) except Exception as err: # If the module log fails, log using stdout/stderr handlers self._logger.error(f"ModuleLogger failed for {record.name} - {err}") diff --git a/src/viam/media/__init__.py b/src/viam/media/__init__.py index fc98358df..e69de29bb 100644 --- a/src/viam/media/__init__.py +++ b/src/viam/media/__init__.py @@ -1,9 +0,0 @@ -from .media import MediaReader, MediaSource, MediaStream, MediaStreamWithIterator, MediaType - -__all__ = [ - "MediaReader", - "MediaSource", - "MediaStream", - "MediaStreamWithIterator", - "MediaType", -] diff --git a/src/viam/media/audio.py b/src/viam/media/audio.py index 35cd91b9f..44a17ac20 100644 --- a/src/viam/media/audio.py +++ b/src/viam/media/audio.py @@ -1,6 +1,6 @@ from dataclasses import dataclass -from viam.media.media import MediaReader, MediaStream +from viam.streams import Stream, StreamReader from viam.proto.component.audioinput import AudioChunk, AudioChunkInfo @@ -12,5 +12,5 @@ class Audio: chunk: AudioChunk -AudioReader = MediaReader[Audio] -AudioStream = MediaStream[Audio] +AudioReader = StreamReader[Audio] +AudioStream = Stream[Audio] diff --git a/src/viam/media/media.py b/src/viam/media/media.py deleted file mode 100644 index 0865c7926..000000000 --- a/src/viam/media/media.py +++ /dev/null @@ -1,47 +0,0 @@ -import sys - -if sys.version_info >= (3, 9): - from collections.abc import AsyncIterator -else: - from typing import AsyncIterator - -from typing import Protocol, TypeVar - -MediaType = TypeVar("MediaType", covariant=True) - - -class MediaStream(Protocol[MediaType]): - async def next(self) -> MediaType: - ... - - def __aiter__(self) -> AsyncIterator: - return self - - async def __anext__(self) -> MediaType: - return await self.next() - - -class MediaReader(Protocol[MediaType]): - async def read(self) -> MediaType: - ... - - -class MediaSource(Protocol[MediaType]): - async def stream(self) -> MediaStream[MediaType]: - ... - - -class MediaStreamWithIterator(MediaStream[MediaType]): - _stream: AsyncIterator[MediaType] - - def __init__(self, stream: AsyncIterator[MediaType]): - self._stream = stream - - async def next(self) -> MediaType: - return await self._stream.__anext__() - - def __aiter__(self): - return self._stream - - async def __anext__(self) -> MediaType: - return await self._stream.__anext__() diff --git a/src/viam/streams.py b/src/viam/streams.py new file mode 100644 index 000000000..45b105280 --- /dev/null +++ b/src/viam/streams.py @@ -0,0 +1,48 @@ +import sys + +if sys.version_info >= (3, 9): + from collections.abc import AsyncIterator +else: + from typing import AsyncIterator + +from typing import Protocol, TypeVar + + +StreamType = TypeVar("StreamType", covariant=True) + + +class Stream(Protocol[StreamType]): + async def next(self) -> StreamType: + ... + + def __aiter__(self) -> AsyncIterator: + return self + + async def __anext__(self) -> StreamType: + return await self.next() + + +class StreamReader(Protocol[StreamType]): + async def read(self) -> StreamType: + ... + + +class StreamSource(Protocol[StreamType]): + async def stream(self) -> Stream[StreamType]: + ... + + +class StreamWithIterator(Stream[StreamType]): + _stream: AsyncIterator[StreamType] + + def __init__(self, stream: AsyncIterator[StreamType]): + self._stream = stream + + async def next(self) -> StreamType: + return await self._stream.__anext__() + + def __aiter__(self): + return self._stream + + async def __anext__(self) -> StreamType: + return await self._stream.__anext__() diff --git a/tests/mocks/components.py b/tests/mocks/components.py index c2003ed5b..d93696f02 100644 --- a/tests/mocks/components.py +++ b/tests/mocks/components.py @@ -16,7 +16,7 @@ from viam.components.arm import Arm, JointPositions, KinematicsFileFormat from viam.components.audio_input import AudioInput from viam.components.base import Base -from viam.components.board import Board +from viam.components.board import Board, Tick from viam.components.camera import Camera, DistortionParameters, IntrinsicParameters from viam.components.encoder import Encoder from viam.components.gantry import Gantry @@ -30,7 +30,7 @@ from viam.components.sensor import Sensor from viam.components.servo import Servo from viam.errors import ResourceNotFoundError -from viam.media import MediaStreamWithIterator +from viam.streams import StreamWithIterator from viam.media.audio import Audio, AudioStream from viam.media.video import CameraMimeType, NamedImage, RawImage from viam.proto.common import ( @@ -143,7 +143,7 @@ async def read() -> AsyncIterator[Audio]: ) self.timeout = timeout - return MediaStreamWithIterator(read()) + return StreamWithIterator(read()) async def get_properties(self, *, timeout: Optional[float] = None, **kwargs) -> AudioInput.Properties: self.timeout = timeout @@ -365,6 +365,14 @@ async def write_analog(self, pin: str, value: int, *, timeout: Optional[float] = self.analog_write_pin = pin self.analog_write_value = value + async def stream_ticks( + self, interrupts: List[Board.DigitalInterrupt], *, timeout: Optional[float] = None, **kwargs + ): + async def read() -> AsyncIterator[Tick]: + yield Tick(pin_name=interrupts[0].name, high=True, time=1000) + + return StreamWithIterator(read()) + class MockCamera(Camera): def __init__(self, name: str): diff --git a/tests/test_board.py b/tests/test_board.py index fce29ff73..880221043 100644 --- a/tests/test_board.py +++ b/tests/test_board.py @@ -34,6 +34,7 @@ SetPowerModeResponse, SetPWMFrequencyRequest, SetPWMRequest, + StreamTicksRequest, WriteAnalogRequest, WriteAnalogResponse, ) @@ -134,6 +135,14 @@ async def test_write_analog(self, board: MockBoard): assert board.analog_write_value == value assert board.analog_write_pin == pin + @pytest.mark.asyncio + async def test_stream_ticks(self, board: MockBoard): + int1 = board.digital_interrupts["interrupt1"] + async for tick in await board.stream_ticks([int1]): + assert tick.pin_name == "interrupt1" + assert tick.time == 1000 + assert tick.high is True + class TestService: @pytest.mark.asyncio @@ -300,6 +309,22 @@ async def test_write_analog(self, board: MockBoard, service: BoardRPCService): assert board.analog_write_value == value assert board.analog_write_pin == pin + # @pytest.mark.asyncio + async def test_stream_ticks(self, board: MockBoard, service: BoardRPCService): + async with ChannelFor([service]) as channel: + client = BoardServiceStub(channel) + interrupts = ["interrupt1"] + extra = {"foo": "stream_ticks"} + request = StreamTicksRequest(name=board.name, pin_names=interrupts, extra=dict_to_struct(extra)) + + async with client.StreamTicks.open(timeout=1) as stream: + await stream.send_message(request, end=True) + resp = await stream.recv_message() + assert resp is not None + assert resp.pin_name == "interrupt1" + assert resp.high is True + assert resp.time == 1000 + class TestClient: @pytest.mark.asyncio @@ -470,3 +495,16 @@ async def test_write_analog(self, board: MockBoard, service: BoardRPCService): await client.write_analog(pin, value, extra=extra) assert board.analog_write_pin == "pin1" assert board.analog_write_value == 42 + + @pytest.mark.asyncio + async def test_stream_ticks(self, board: MockBoard, service: BoardRPCService): + async with ChannelFor([service]) as channel: + client = BoardClient(name=board.name, channel=channel) + di = await client.digital_interrupt_by_name("interrupt1") + + tick_stream = await client.stream_ticks(interrupts=[di]) + async for tick in tick_stream: + assert tick.pin_name == 'interrupt1' + assert tick.high is True + assert tick.time == 1000 + break diff --git a/tests/test_power_sensor.py b/tests/test_power_sensor.py index 56d08a129..72b99bde0 100644 --- a/tests/test_power_sensor.py +++ b/tests/test_power_sensor.py @@ -76,8 +76,8 @@ async def test_get_readings(self, power_sensor: MockPowerSensor): async def test_timeout(self, power_sensor: MockPowerSensor): assert power_sensor.timeout is None - await power_sensor.get_voltage(timeout=1.23) - assert power_sensor.timeout == loose_approx(1.23) + await power_sensor.get_voltage(timeout=8.90) + assert power_sensor.timeout == loose_approx(8.90) await power_sensor.get_current(timeout=2.34) assert power_sensor.timeout == loose_approx(2.34) @@ -102,11 +102,11 @@ async def test_get_voltage(self, power_sensor: MockPowerSensor, service: PowerSe client = PowerSensorServiceStub(channel) request = GetVoltageRequest(name=power_sensor.name, extra=dict_to_struct(EXTRA_PARAMS)) assert power_sensor.extra is None - response: GetVoltageResponse = await client.GetVoltage(request, timeout=1.23) + response: GetVoltageResponse = await client.GetVoltage(request, timeout=8.90) assert response.volts == VOLTS assert response.is_ac == IS_AC assert power_sensor.extra == EXTRA_PARAMS - assert power_sensor.timeout == loose_approx(1.23) + assert power_sensor.timeout == loose_approx(8.90) @pytest.mark.asyncio async def test_get_current(self, power_sensor: MockPowerSensor, service: PowerSensorRPCService): @@ -114,11 +114,11 @@ async def test_get_current(self, power_sensor: MockPowerSensor, service: PowerSe client = PowerSensorServiceStub(channel) request = GetCurrentRequest(name=power_sensor.name, extra=dict_to_struct(EXTRA_PARAMS)) assert power_sensor.extra is None - response: GetCurrentResponse = await client.GetCurrent(request, timeout=1.23) + response: GetCurrentResponse = await client.GetCurrent(request, timeout=8.90) assert response.amperes == AMPERES assert response.is_ac == IS_AC assert power_sensor.extra == EXTRA_PARAMS - assert power_sensor.timeout == loose_approx(1.23) + assert power_sensor.timeout == loose_approx(8.90) @pytest.mark.asyncio async def test_get_power(self, power_sensor: MockPowerSensor, service: PowerSensorRPCService): @@ -126,10 +126,10 @@ async def test_get_power(self, power_sensor: MockPowerSensor, service: PowerSens client = PowerSensorServiceStub(channel) request = GetPowerRequest(name=power_sensor.name, extra=dict_to_struct(EXTRA_PARAMS)) assert power_sensor.extra is None - response: GetPowerResponse = await client.GetPower(request, timeout=1.23) + response: GetPowerResponse = await client.GetPower(request, timeout=8.90) assert response.watts == WATTS assert power_sensor.extra == EXTRA_PARAMS - assert power_sensor.timeout == loose_approx(1.23) + assert power_sensor.timeout == loose_approx(8.90) @pytest.mark.asyncio async def test_get_readings(self, power_sensor: MockPowerSensor, service: PowerSensorRPCService): diff --git a/tests/test_robot.py b/tests/test_robot.py index 069614d09..7e067dc40 100644 --- a/tests/test_robot.py +++ b/tests/test_robot.py @@ -143,9 +143,11 @@ OPERATIONS_RESPONSE = [Operation(id=OPERATION_ID)] GET_CLOUD_METADATA_RESPONSE = GetCloudMetadataResponse( - robot_part_id="the-robot-part", + robot_part_id="the-machine-id", primary_org_id="the-primary-org", location_id="the-location", + machine_id="the-machine-id", + machine_part_id="the-machine-part-id", )