diff --git a/docs/examples/create_device_with_issued_sim.py b/docs/examples/create_device_with_issued_sim.py new file mode 100644 index 0000000..df9633c --- /dev/null +++ b/docs/examples/create_device_with_issued_sim.py @@ -0,0 +1,28 @@ +# * Import SDK package +from emnify import EMnify + +# Initiate instance + +emnify_client = EMnify("YOUR APP TOKEN") +""" +=== Create a disabled device with a SIM that stays in ISSUED status === +""" +# When a sim object is provided the API activates the SIM by default, and an +# activated SIM is billed even while the device is disabled. Pass +# `activate: False` to assign the SIM without activating it. +service_profile = emnify_client.devices.service_profile_model(id=1) +tariff_profile = emnify_client.devices.tariff_profile_model(id=1) +device_status = emnify_client.devices.status_model(id=1, description="Disabled") +device_model = emnify_client.devices.device_create_model( + name="my-device", + tariff_profile=tariff_profile, + status=device_status, + service_profile=service_profile, + # pass a plain dict - the SimList model has no `activate` field and would drop it + sim={"id": 1, "activate": False}, +) + +device_id = emnify_client.devices.create_device(device_model) + +# The SIM is assigned to the device and still in ISSUED status +print(emnify_client.sim.retrieve_sim(sim_id=1).status) diff --git a/docs/examples/device_lifecycle_management.py b/docs/examples/device_lifecycle_management.py index fa26967..4c94825 100644 --- a/docs/examples/device_lifecycle_management.py +++ b/docs/examples/device_lifecycle_management.py @@ -132,8 +132,10 @@ device_to_delete = list( filter( - lambda device: device.sim - and device.status.id == emnify_constants.DeviceStatuses.ENABLED_ID, + lambda device: ( + device.sim + and device.status.id == emnify_constants.DeviceStatuses.ENABLED_ID + ), old_devices_list, ) )[0] diff --git a/docs/examples/local_debug.py b/docs/examples/local_debug.py index 55203c0..b606770 100644 --- a/docs/examples/local_debug.py +++ b/docs/examples/local_debug.py @@ -5,19 +5,21 @@ import sys # Add the path to the module to the Python path, below it's set to Docker workdir -sys.path.append('/sdk') +sys.path.append("/sdk") # Import package from emnify import EMnify # Get the token from environment variable -TOKEN = os.environ.get('EMNIFY_SDK_APPLICATION_TOKEN') +TOKEN = os.environ.get("EMNIFY_SDK_APPLICATION_TOKEN") # Initiate SDK instance using application token emnify = EMnify(TOKEN) + def get_iterator_length(iterator): return len(list(iterator)) + # Execute a command against desired API devices = emnify.devices.get_devices_list() # diff --git a/emnify/modules/device/models.py b/emnify/modules/device/models.py index 8f83faa..767ac5e 100644 --- a/emnify/modules/device/models.py +++ b/emnify/modules/device/models.py @@ -10,6 +10,7 @@ class SimDevice(BaseModel): id: int status: Optional[generated_models.Status] = None + activate: Optional[bool] = None class Device(generated_models.Endpoint): diff --git a/pyproject.toml b/pyproject.toml index b0f0765..9aecf1d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,9 @@ [build-system] requires = ["setuptools>=42"] build-backend = "setuptools.build_meta" + +[tool.ruff] +extend-exclude = ["emnify/version.py"] + +[tool.ruff.lint] +select = ["E4", "E7", "E9", "F"] diff --git a/tests/test_emnify.py b/tests/test_emnify.py index fd489f8..328b6a3 100644 --- a/tests/test_emnify.py +++ b/tests/test_emnify.py @@ -170,6 +170,32 @@ def test_create_device_with_sim(self): response = emnify.devices.create_device(device=device) self.assertEqual(response, 11380016) + def test_create_device_sim_activate_is_serialized(self): + """`sim.activate` must reach the request body so callers can assign a + SIM without activating it.""" + emnify = emnify_client(app_token=self.token) + + def build_sim_payload(sim): + device = emnify.devices.device_create_model( + name="test-device", + status=emnify.devices.status_model(id=1, description="Disabled"), + service_profile=emnify.devices.service_profile_model(id=1), + tariff_profile=emnify.devices.tariff_profile_model(id=2), + sim=sim, + ) + return device.dict(exclude_none=True)["sim"] + + self.assertEqual( + build_sim_payload({"id": 250, "activate": False}), + {"id": 250, "activate": False}, + ) + self.assertEqual( + build_sim_payload({"id": 250, "activate": True}), + {"id": 250, "activate": True}, + ) + # omitted -> not sent, so the API default applies + self.assertEqual(build_sim_payload({"id": 250}), {"id": 250}) + @vcr_instance.use_cassette( "tests/fixtures/cassettes/test_activate_disactivate_device.yaml" )