hyxi-cloud-api 1.3.6__tar.gz → 1.3.8__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {hyxi_cloud_api-1.3.6/src/hyxi_cloud_api.egg-info → hyxi_cloud_api-1.3.8}/PKG-INFO +1 -1
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/pyproject.toml +1 -1
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/src/hyxi_cloud_api/__init__.py +1 -1
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/src/hyxi_cloud_api/api.py +52 -6
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8/src/hyxi_cloud_api.egg-info}/PKG-INFO +1 -1
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/tests/test_compute_battery_metrics.py +52 -0
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/tests/test_device_control.py +24 -0
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/tests/test_subscriptions.py +57 -0
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/LICENSE +0 -0
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/README.md +0 -0
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/setup.cfg +0 -0
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/src/hyxi_cloud_api.egg-info/SOURCES.txt +0 -0
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/src/hyxi_cloud_api.egg-info/dependency_links.txt +0 -0
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/src/hyxi_cloud_api.egg-info/requires.txt +0 -0
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/src/hyxi_cloud_api.egg-info/top_level.txt +0 -0
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/tests/test_additional_coverage.py +0 -0
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/tests/test_alarm_push.py +0 -0
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/tests/test_all_in_one.py +0 -0
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/tests/test_api.py +0 -0
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/tests/test_build_plant_tasks.py +0 -0
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/tests/test_caching.py +0 -0
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/tests/test_compute_derived_metrics.py +0 -0
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/tests/test_compute_grid_metrics.py +0 -0
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/tests/test_compute_load_metrics.py +0 -0
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/tests/test_device_entry.py +0 -0
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/tests/test_devices_errors.py +0 -0
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/tests/test_discovery.py +0 -0
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/tests/test_execute_device_tasks.py +0 -0
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/tests/test_execute_metric_tasks.py +0 -0
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/tests/test_execute_metrics_and_map_alarms.py +0 -0
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/tests/test_extract_battery_info.py +0 -0
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/tests/test_extract_device_info_metadata.py +0 -0
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/tests/test_fetch_and_process_alarms.py +0 -0
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/tests/test_fetch_device_list_for_plant.py +0 -0
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/tests/test_fetch_plants.py +0 -0
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/tests/test_fetch_state.py +0 -0
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/tests/test_fetch_sub_device_list.py +0 -0
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/tests/test_filter_metrics.py +0 -0
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/tests/test_fuzz_parser.py +0 -0
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/tests/test_get_f.py +0 -0
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/tests/test_handle_back_discovery_alarm.py +0 -0
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/tests/test_info_errors.py +0 -0
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/tests/test_mask_id.py +0 -0
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/tests/test_metrics_errors.py +0 -0
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/tests/test_parse_data_list.py +0 -0
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/tests/test_parse_ems_kv.py +0 -0
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/tests/test_parser.py +0 -0
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/tests/test_process_alarms_and_back_discovery.py +0 -0
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/tests/test_process_devices_for_plant.py +0 -0
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/tests/test_push.py +0 -0
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/tests/test_sanitize_dict.py +0 -0
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/tests/test_sanitize_list.py +0 -0
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/tests/test_security_fix.py +0 -0
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/tests/test_token_errors.py +0 -0
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/tests/test_token_handling.py +0 -0
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/tests/test_validate_post_rate_ms.py +0 -0
- {hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/tests/test_validate_subscription_device_sns.py +0 -0
|
@@ -5,5 +5,5 @@ from .api import HyxiApiClient
|
|
|
5
5
|
# Module-level alias so callers can do: from hyxi_cloud_api import VPP_ACTIVE_MODES
|
|
6
6
|
VPP_ACTIVE_MODES: frozenset[str] = HyxiApiClient.VPP_ACTIVE_MODES
|
|
7
7
|
|
|
8
|
-
__version__ = "1.3.
|
|
8
|
+
__version__ = "1.3.8"
|
|
9
9
|
__all__ = ["VPP_ACTIVE_MODES", "HyxiApiClient"]
|
|
@@ -72,6 +72,8 @@ _COLLECTOR_FILTER_REGEX = re.compile(
|
|
|
72
72
|
|
|
73
73
|
_PV_KEYS = tuple((f"pv{i}v", f"pv{i}i", f"pv{i}p") for i in range(1, 5))
|
|
74
74
|
|
|
75
|
+
_METRICS_EXCLUDED_KEYS = frozenset({"deviceSn", "reportTimestamp", "collectTime"})
|
|
76
|
+
|
|
75
77
|
# Official HYXI Alarm Code Reference Table
|
|
76
78
|
ALARM_CODE_MAP = {
|
|
77
79
|
"704": "The ambient temperature is too high",
|
|
@@ -634,10 +636,54 @@ def _compute_battery_metrics(
|
|
|
634
636
|
derived["bat_discharging"] = power_source if power_source > 0 else 0.0
|
|
635
637
|
derived["bat_power_dc"] = bat_p_dc
|
|
636
638
|
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
639
|
+
def _is_valid_metric(k: str) -> bool:
|
|
640
|
+
v = m_raw.get(k)
|
|
641
|
+
if v is None:
|
|
642
|
+
return False
|
|
643
|
+
if isinstance(v, str):
|
|
644
|
+
v_cleaned = v.strip().lower()
|
|
645
|
+
return bool(v_cleaned and v_cleaned not in ("null", "none", "na", "--"))
|
|
646
|
+
return True
|
|
647
|
+
|
|
648
|
+
# Unify cumulative battery charge energy telemetry keys to resolve polling/push mismatch
|
|
649
|
+
total_echg_val = (
|
|
650
|
+
_get_f("totalEchg", m_raw) if _is_valid_metric("totalEchg") else None
|
|
651
|
+
)
|
|
652
|
+
bat_charge_val = (
|
|
653
|
+
_get_f("batCharge", m_raw) if _is_valid_metric("batCharge") else None
|
|
654
|
+
)
|
|
655
|
+
|
|
656
|
+
if total_echg_val is not None:
|
|
657
|
+
derived["totalEchg"] = total_echg_val
|
|
658
|
+
derived["bat_charge_total"] = total_echg_val
|
|
659
|
+
if bat_charge_val is None:
|
|
660
|
+
derived["batCharge"] = total_echg_val
|
|
661
|
+
|
|
662
|
+
if bat_charge_val is not None:
|
|
663
|
+
derived["batCharge"] = bat_charge_val
|
|
664
|
+
if total_echg_val is None:
|
|
665
|
+
derived["totalEchg"] = bat_charge_val
|
|
666
|
+
derived["bat_charge_total"] = bat_charge_val
|
|
667
|
+
|
|
668
|
+
# Unify cumulative battery discharge energy telemetry keys to resolve polling/push mismatch
|
|
669
|
+
total_edchg_val = (
|
|
670
|
+
_get_f("totalEdchg", m_raw) if _is_valid_metric("totalEdchg") else None
|
|
671
|
+
)
|
|
672
|
+
bat_discharge_val = (
|
|
673
|
+
_get_f("batDisCharge", m_raw) if _is_valid_metric("batDisCharge") else None
|
|
674
|
+
)
|
|
675
|
+
|
|
676
|
+
if total_edchg_val is not None:
|
|
677
|
+
derived["totalEdchg"] = total_edchg_val
|
|
678
|
+
derived["bat_discharge_total"] = total_edchg_val
|
|
679
|
+
if bat_discharge_val is None:
|
|
680
|
+
derived["batDisCharge"] = total_edchg_val
|
|
681
|
+
|
|
682
|
+
if bat_discharge_val is not None:
|
|
683
|
+
derived["batDisCharge"] = bat_discharge_val
|
|
684
|
+
if total_edchg_val is None:
|
|
685
|
+
derived["totalEdchg"] = bat_discharge_val
|
|
686
|
+
derived["bat_discharge_total"] = bat_discharge_val
|
|
641
687
|
|
|
642
688
|
|
|
643
689
|
def _compute_pv_metrics(m_raw: dict, derived: dict[str, float]) -> None:
|
|
@@ -2149,7 +2195,7 @@ class HyxiApiClient: # pylint: disable=too-many-instance-attributes
|
|
|
2149
2195
|
# Strip metadata/routing keys
|
|
2150
2196
|
raw_metrics = {}
|
|
2151
2197
|
for k, v in device.items():
|
|
2152
|
-
if k in
|
|
2198
|
+
if k in _METRICS_EXCLUDED_KEYS:
|
|
2153
2199
|
continue
|
|
2154
2200
|
raw_metrics[k] = v
|
|
2155
2201
|
|
|
@@ -2250,7 +2296,7 @@ class HyxiApiClient: # pylint: disable=too-many-instance-attributes
|
|
|
2250
2296
|
if not sn:
|
|
2251
2297
|
continue
|
|
2252
2298
|
|
|
2253
|
-
alarm_code =
|
|
2299
|
+
alarm_code = f"{item.get('alarmCode', '')}"
|
|
2254
2300
|
|
|
2255
2301
|
# Resolve alarm name: use payload name if present, fall back to ALARM_CODE_MAP
|
|
2256
2302
|
alarm_name = item.get("alarmName") or ALARM_CODE_MAP.get(
|
|
@@ -60,6 +60,37 @@ def test_compute_battery_metrics_totals():
|
|
|
60
60
|
_compute_battery_metrics(m_raw, derived, "OTHER")
|
|
61
61
|
assert derived["bat_charge_total"] == 12.5
|
|
62
62
|
assert derived["bat_discharge_total"] == 45.6
|
|
63
|
+
assert derived["totalEchg"] == 12.5
|
|
64
|
+
assert derived["totalEdchg"] == 45.6
|
|
65
|
+
assert derived["batCharge"] == 12.5
|
|
66
|
+
assert derived["batDisCharge"] == 45.6
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def test_compute_battery_metrics_polling_keys():
|
|
70
|
+
"""Test fallback and synchronization when only polling keys are present."""
|
|
71
|
+
derived: dict[str, float] = {}
|
|
72
|
+
m_raw = {"totalEchg": 100.5, "totalEdchg": 200.2}
|
|
73
|
+
_compute_battery_metrics(m_raw, derived, "OTHER")
|
|
74
|
+
assert derived["bat_charge_total"] == 100.5
|
|
75
|
+
assert derived["bat_discharge_total"] == 200.2
|
|
76
|
+
assert derived["totalEchg"] == 100.5
|
|
77
|
+
assert derived["totalEdchg"] == 200.2
|
|
78
|
+
assert derived["batCharge"] == 100.5
|
|
79
|
+
assert derived["batDisCharge"] == 200.2
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def test_compute_battery_metrics_null_and_empty_handling():
|
|
83
|
+
"""Verify that null-equivalent and empty values do not pollute derived metrics."""
|
|
84
|
+
derived: dict[str, float] = {}
|
|
85
|
+
m_raw = {
|
|
86
|
+
"batCharge": 150.0,
|
|
87
|
+
"totalEchg": "null",
|
|
88
|
+
"batDisCharge": "None",
|
|
89
|
+
"totalEdchg": 250.0,
|
|
90
|
+
}
|
|
91
|
+
_compute_battery_metrics(m_raw, derived, "OTHER")
|
|
92
|
+
assert derived["bat_charge_total"] == 150.0
|
|
93
|
+
assert derived["bat_discharge_total"] == 250.0
|
|
63
94
|
|
|
64
95
|
|
|
65
96
|
def test_compute_battery_metrics_empty():
|
|
@@ -76,3 +107,24 @@ def test_compute_battery_metrics_none_device_type():
|
|
|
76
107
|
_compute_battery_metrics(m_raw, derived, None) # type: ignore
|
|
77
108
|
assert derived["bat_charging"] == 100.0
|
|
78
109
|
assert derived["bat_power_dc"] == -100.0
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def test_compute_battery_metrics_both_present():
|
|
113
|
+
"""Test that when both polling and daily keys are present, they are preserved.
|
|
114
|
+
|
|
115
|
+
Cumulative values should take priority for battery totals.
|
|
116
|
+
"""
|
|
117
|
+
derived: dict[str, float] = {}
|
|
118
|
+
m_raw = {
|
|
119
|
+
"totalEchg": 366.3,
|
|
120
|
+
"totalEdchg": 371.3,
|
|
121
|
+
"batCharge": 2.3,
|
|
122
|
+
"batDisCharge": 4.4,
|
|
123
|
+
}
|
|
124
|
+
_compute_battery_metrics(m_raw, derived, "OTHER")
|
|
125
|
+
assert derived["bat_charge_total"] == 366.3
|
|
126
|
+
assert derived["bat_discharge_total"] == 371.3
|
|
127
|
+
assert derived["totalEchg"] == 366.3
|
|
128
|
+
assert derived["totalEdchg"] == 371.3
|
|
129
|
+
assert derived["batCharge"] == 2.3
|
|
130
|
+
assert derived["batDisCharge"] == 4.4
|
|
@@ -221,6 +221,30 @@ async def test_set_micro_power_limit_invalid_percentage():
|
|
|
221
221
|
await api.set_micro_power_limit("SN123", percentage=-1)
|
|
222
222
|
|
|
223
223
|
|
|
224
|
+
@pytest.mark.asyncio
|
|
225
|
+
async def test_set_micro_power_limit_mocked_control():
|
|
226
|
+
"""Test set_micro_power_limit by mocking set_device_control and checking edge cases."""
|
|
227
|
+
api = HyxiApiClient("ak", "sk", "https://api.com", MagicMock())
|
|
228
|
+
api.set_device_control = AsyncMock(return_value={"success": True})
|
|
229
|
+
|
|
230
|
+
# Test 80%
|
|
231
|
+
result = await api.set_micro_power_limit("SN123", percentage=80)
|
|
232
|
+
api.set_device_control.assert_awaited_once_with("SN123", {3012: "80"})
|
|
233
|
+
assert result == {"success": True}
|
|
234
|
+
|
|
235
|
+
# Test boundary 0%
|
|
236
|
+
api.set_device_control.reset_mock()
|
|
237
|
+
result = await api.set_micro_power_limit("SN123", percentage=0)
|
|
238
|
+
api.set_device_control.assert_awaited_once_with("SN123", {3012: "0"})
|
|
239
|
+
assert result == {"success": True}
|
|
240
|
+
|
|
241
|
+
# Test boundary 100%
|
|
242
|
+
api.set_device_control.reset_mock()
|
|
243
|
+
result = await api.set_micro_power_limit("SN123", percentage=100)
|
|
244
|
+
api.set_device_control.assert_awaited_once_with("SN123", {3012: "100"})
|
|
245
|
+
assert result == {"success": True}
|
|
246
|
+
|
|
247
|
+
|
|
224
248
|
@pytest.mark.asyncio
|
|
225
249
|
async def test_restart_device():
|
|
226
250
|
"""Test restart_device sends controlId 3013 with value '1'."""
|
|
@@ -57,6 +57,46 @@ async def test_subscribe_real_time_data():
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
|
|
60
|
+
@pytest.mark.asyncio
|
|
61
|
+
async def test_subscribe_real_time_data_mock_post_subscription():
|
|
62
|
+
"""Test real-time data subscription by mocking _post_subscription."""
|
|
63
|
+
api = _client()
|
|
64
|
+
api._post_subscription = AsyncMock(return_value={"success": True, "data": "mocked"})
|
|
65
|
+
|
|
66
|
+
# Test with data_code_list
|
|
67
|
+
result = await api.subscribe_real_time_data(
|
|
68
|
+
callback_url="https://example.com/hyxi",
|
|
69
|
+
device_sn_list=["SN1"],
|
|
70
|
+
post_rate=5000,
|
|
71
|
+
data_code_list=["pv1p"],
|
|
72
|
+
)
|
|
73
|
+
assert result == {"success": True, "data": "mocked"}
|
|
74
|
+
api._post_subscription.assert_called_with(
|
|
75
|
+
"/api/subscribe/v1/realTimeData",
|
|
76
|
+
{
|
|
77
|
+
"callBackUrl": "https://example.com/hyxi",
|
|
78
|
+
"deviceSnList": ["SN1"],
|
|
79
|
+
"postRate": 5000,
|
|
80
|
+
"dataCodeList": ["pv1p"],
|
|
81
|
+
},
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
# Test without data_code_list
|
|
85
|
+
result = await api.subscribe_real_time_data(
|
|
86
|
+
callback_url="https://example.com/hyxi",
|
|
87
|
+
device_sn_list=["SN1"],
|
|
88
|
+
post_rate=5000,
|
|
89
|
+
)
|
|
90
|
+
api._post_subscription.assert_called_with(
|
|
91
|
+
"/api/subscribe/v1/realTimeData",
|
|
92
|
+
{
|
|
93
|
+
"callBackUrl": "https://example.com/hyxi",
|
|
94
|
+
"deviceSnList": ["SN1"],
|
|
95
|
+
"postRate": 5000,
|
|
96
|
+
},
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
|
|
60
100
|
@pytest.mark.asyncio
|
|
61
101
|
async def test_subscribe_alarm():
|
|
62
102
|
"""Test alarm subscription payload."""
|
|
@@ -109,6 +149,23 @@ async def test_cancel_subscription():
|
|
|
109
149
|
assert call_args.kwargs["json"] == {"subscribeCode": "sub-code"}
|
|
110
150
|
|
|
111
151
|
|
|
152
|
+
@pytest.mark.asyncio
|
|
153
|
+
async def test_cancel_subscription_return_value():
|
|
154
|
+
"""Test subscription cancellation return value matches expected API response."""
|
|
155
|
+
api = _client()
|
|
156
|
+
|
|
157
|
+
# Execute the method
|
|
158
|
+
result = await api.cancel_subscription("valid-sub-code")
|
|
159
|
+
|
|
160
|
+
# Verify the return payload matches the API response
|
|
161
|
+
assert result == {
|
|
162
|
+
"code": "0",
|
|
163
|
+
"msg": "Success",
|
|
164
|
+
"data": {"subscribeCode": "sub-code"},
|
|
165
|
+
"success": True,
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
|
|
112
169
|
@pytest.mark.asyncio
|
|
113
170
|
async def test_subscription_error_on_auth_failed():
|
|
114
171
|
"""Test SubscriptionError is raised when authentication fails."""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/src/hyxi_cloud_api.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/tests/test_process_alarms_and_back_discovery.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hyxi_cloud_api-1.3.6 → hyxi_cloud_api-1.3.8}/tests/test_validate_subscription_device_sns.py
RENAMED
|
File without changes
|