Skip to content

Commit b581592

Browse files
Kevin O'Brienirishpadres
authored andcommitted
Fix Proxmox VE sensor crashes due to missing API fields
Update all sensor definitions (Node, VM, Container, and Storage) to safely handle missing keys in the Proxmox API response by using dict.get(). This prevents KeyError crashes for entities where certain fields (like used_fraction, cpu, or memory) may be omitted. Added tests/components/proxmoxve/test_sensor_missing_data.py to verify robust handling of incomplete API data.
1 parent eb64589 commit b581592

File tree

2 files changed

+156
-37
lines changed

2 files changed

+156
-37
lines changed

homeassistant/components/proxmoxve/sensor.py

Lines changed: 58 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,9 @@ class ProxmoxStorageSensorEntityDescription(SensorEntityDescription):
6363
ProxmoxNodeSensorEntityDescription(
6464
key="node_cpu",
6565
translation_key="node_cpu",
66-
value_fn=lambda data: data.node["cpu"] * 100,
66+
value_fn=lambda data: (
67+
value * 100 if (value := data.node.get("cpu")) is not None else None
68+
),
6769
native_unit_of_measurement=PERCENTAGE,
6870
entity_category=EntityCategory.DIAGNOSTIC,
6971
suggested_display_precision=2,
@@ -72,12 +74,12 @@ class ProxmoxStorageSensorEntityDescription(SensorEntityDescription):
7274
ProxmoxNodeSensorEntityDescription(
7375
key="node_max_cpu",
7476
translation_key="node_max_cpu",
75-
value_fn=lambda data: data.node["maxcpu"],
77+
value_fn=lambda data: data.node.get("maxcpu"),
7678
),
7779
ProxmoxNodeSensorEntityDescription(
7880
key="node_disk",
7981
translation_key="node_disk",
80-
value_fn=lambda data: data.node["disk"],
82+
value_fn=lambda data: data.node.get("disk"),
8183
device_class=SensorDeviceClass.DATA_SIZE,
8284
native_unit_of_measurement=UnitOfInformation.BYTES,
8385
suggested_unit_of_measurement=UnitOfInformation.GIBIBYTES,
@@ -88,7 +90,7 @@ class ProxmoxStorageSensorEntityDescription(SensorEntityDescription):
8890
ProxmoxNodeSensorEntityDescription(
8991
key="node_max_disk",
9092
translation_key="node_max_disk",
91-
value_fn=lambda data: data.node["maxdisk"],
93+
value_fn=lambda data: data.node.get("maxdisk"),
9294
device_class=SensorDeviceClass.DATA_SIZE,
9395
native_unit_of_measurement=UnitOfInformation.BYTES,
9496
suggested_unit_of_measurement=UnitOfInformation.GIBIBYTES,
@@ -99,7 +101,7 @@ class ProxmoxStorageSensorEntityDescription(SensorEntityDescription):
99101
ProxmoxNodeSensorEntityDescription(
100102
key="node_memory",
101103
translation_key="node_memory",
102-
value_fn=lambda data: data.node["mem"],
104+
value_fn=lambda data: data.node.get("mem"),
103105
device_class=SensorDeviceClass.DATA_SIZE,
104106
native_unit_of_measurement=UnitOfInformation.BYTES,
105107
suggested_unit_of_measurement=UnitOfInformation.GIBIBYTES,
@@ -110,7 +112,7 @@ class ProxmoxStorageSensorEntityDescription(SensorEntityDescription):
110112
ProxmoxNodeSensorEntityDescription(
111113
key="node_max_memory",
112114
translation_key="node_max_memory",
113-
value_fn=lambda data: data.node["maxmem"],
115+
value_fn=lambda data: data.node.get("maxmem"),
114116
device_class=SensorDeviceClass.DATA_SIZE,
115117
native_unit_of_measurement=UnitOfInformation.BYTES,
116118
suggested_unit_of_measurement=UnitOfInformation.GIBIBYTES,
@@ -121,7 +123,12 @@ class ProxmoxStorageSensorEntityDescription(SensorEntityDescription):
121123
ProxmoxNodeSensorEntityDescription(
122124
key="node_memory_percentage",
123125
translation_key="node_memory_percentage",
124-
value_fn=lambda data: int(data.node["mem"]) / int(data.node["maxmem"]) * 100,
126+
value_fn=lambda data: (
127+
int(mem) / int(maxmem) * 100
128+
if (mem := data.node.get("mem")) is not None
129+
and (maxmem := data.node.get("maxmem"))
130+
else None
131+
),
125132
native_unit_of_measurement=PERCENTAGE,
126133
entity_category=EntityCategory.DIAGNOSTIC,
127134
suggested_display_precision=2,
@@ -130,7 +137,7 @@ class ProxmoxStorageSensorEntityDescription(SensorEntityDescription):
130137
ProxmoxNodeSensorEntityDescription(
131138
key="node_uptime",
132139
translation_key="node_uptime",
133-
value_fn=lambda data: data.node["uptime"],
140+
value_fn=lambda data: data.node.get("uptime"),
134141
device_class=SensorDeviceClass.DURATION,
135142
native_unit_of_measurement=UnitOfTime.SECONDS,
136143
suggested_unit_of_measurement=UnitOfTime.HOURS,
@@ -140,16 +147,16 @@ class ProxmoxStorageSensorEntityDescription(SensorEntityDescription):
140147
ProxmoxNodeSensorEntityDescription(
141148
key="node_status",
142149
translation_key="node_status",
143-
value_fn=lambda data: data.node["status"],
150+
value_fn=lambda data: data.node.get("status"),
144151
device_class=SensorDeviceClass.ENUM,
145152
options=["online", "offline"],
146153
),
147154
ProxmoxNodeSensorEntityDescription(
148155
key="node_backup_last_backup",
149156
translation_key="node_backup_last_backup",
150157
value_fn=lambda data: (
151-
dt_util.utc_from_timestamp(data.backups[0]["endtime"])
152-
if data.backups
158+
dt_util.utc_from_timestamp(endtime)
159+
if data.backups and (endtime := data.backups[0].get("endtime")) is not None
153160
else None
154161
),
155162
device_class=SensorDeviceClass.TIMESTAMP,
@@ -159,8 +166,10 @@ class ProxmoxStorageSensorEntityDescription(SensorEntityDescription):
159166
key="node_backup_duration",
160167
translation_key="node_backup_duration",
161168
value_fn=lambda data: (
162-
data.backups[0]["endtime"] - data.backups[0]["starttime"]
169+
endtime - starttime
163170
if data.backups
171+
and (endtime := data.backups[0].get("endtime")) is not None
172+
and (starttime := data.backups[0].get("starttime")) is not None
164173
else None
165174
),
166175
device_class=SensorDeviceClass.DURATION,
@@ -175,12 +184,14 @@ class ProxmoxStorageSensorEntityDescription(SensorEntityDescription):
175184
ProxmoxVMSensorEntityDescription(
176185
key="vm_max_cpu",
177186
translation_key="vm_max_cpu",
178-
value_fn=lambda data: data["cpus"],
187+
value_fn=lambda data: data.get("cpus"),
179188
),
180189
ProxmoxVMSensorEntityDescription(
181190
key="vm_cpu",
182191
translation_key="vm_cpu",
183-
value_fn=lambda data: data["cpu"] * 100,
192+
value_fn=lambda data: (
193+
value * 100 if (value := data.get("cpu")) is not None else None
194+
),
184195
native_unit_of_measurement=PERCENTAGE,
185196
entity_category=EntityCategory.DIAGNOSTIC,
186197
suggested_display_precision=2,
@@ -189,7 +200,7 @@ class ProxmoxStorageSensorEntityDescription(SensorEntityDescription):
189200
ProxmoxVMSensorEntityDescription(
190201
key="vm_memory",
191202
translation_key="vm_memory",
192-
value_fn=lambda data: data["mem"],
203+
value_fn=lambda data: data.get("mem"),
193204
device_class=SensorDeviceClass.DATA_SIZE,
194205
native_unit_of_measurement=UnitOfInformation.BYTES,
195206
suggested_unit_of_measurement=UnitOfInformation.GIBIBYTES,
@@ -200,7 +211,7 @@ class ProxmoxStorageSensorEntityDescription(SensorEntityDescription):
200211
ProxmoxVMSensorEntityDescription(
201212
key="vm_max_memory",
202213
translation_key="vm_max_memory",
203-
value_fn=lambda data: data["maxmem"],
214+
value_fn=lambda data: data.get("maxmem"),
204215
device_class=SensorDeviceClass.DATA_SIZE,
205216
native_unit_of_measurement=UnitOfInformation.BYTES,
206217
suggested_unit_of_measurement=UnitOfInformation.GIBIBYTES,
@@ -211,7 +222,11 @@ class ProxmoxStorageSensorEntityDescription(SensorEntityDescription):
211222
ProxmoxVMSensorEntityDescription(
212223
key="vm_memory_percentage",
213224
translation_key="vm_memory_percentage",
214-
value_fn=lambda data: int(data["mem"]) / int(data["maxmem"]) * 100,
225+
value_fn=lambda data: (
226+
int(mem) / int(maxmem) * 100
227+
if (mem := data.get("mem")) is not None and (maxmem := data.get("maxmem"))
228+
else None
229+
),
215230
native_unit_of_measurement=PERCENTAGE,
216231
entity_category=EntityCategory.DIAGNOSTIC,
217232
suggested_display_precision=2,
@@ -220,7 +235,7 @@ class ProxmoxStorageSensorEntityDescription(SensorEntityDescription):
220235
ProxmoxVMSensorEntityDescription(
221236
key="vm_uptime",
222237
translation_key="vm_uptime",
223-
value_fn=lambda data: data["uptime"],
238+
value_fn=lambda data: data.get("uptime"),
224239
device_class=SensorDeviceClass.DURATION,
225240
native_unit_of_measurement=UnitOfTime.SECONDS,
226241
suggested_unit_of_measurement=UnitOfTime.HOURS,
@@ -230,7 +245,7 @@ class ProxmoxStorageSensorEntityDescription(SensorEntityDescription):
230245
ProxmoxVMSensorEntityDescription(
231246
key="vm_disk",
232247
translation_key="vm_disk",
233-
value_fn=lambda data: data["disk"],
248+
value_fn=lambda data: data.get("disk"),
234249
device_class=SensorDeviceClass.DATA_SIZE,
235250
native_unit_of_measurement=UnitOfInformation.BYTES,
236251
suggested_unit_of_measurement=UnitOfInformation.GIBIBYTES,
@@ -241,7 +256,7 @@ class ProxmoxStorageSensorEntityDescription(SensorEntityDescription):
241256
ProxmoxVMSensorEntityDescription(
242257
key="vm_max_disk",
243258
translation_key="vm_max_disk",
244-
value_fn=lambda data: data["maxdisk"],
259+
value_fn=lambda data: data.get("maxdisk"),
245260
device_class=SensorDeviceClass.DATA_SIZE,
246261
native_unit_of_measurement=UnitOfInformation.BYTES,
247262
suggested_unit_of_measurement=UnitOfInformation.GIBIBYTES,
@@ -252,14 +267,14 @@ class ProxmoxStorageSensorEntityDescription(SensorEntityDescription):
252267
ProxmoxVMSensorEntityDescription(
253268
key="vm_status",
254269
translation_key="vm_status",
255-
value_fn=lambda data: data["status"],
270+
value_fn=lambda data: data.get("status"),
256271
device_class=SensorDeviceClass.ENUM,
257272
options=["running", "stopped", "suspended"],
258273
),
259274
ProxmoxVMSensorEntityDescription(
260275
key="vm_netin",
261276
translation_key="vm_netin",
262-
value_fn=lambda data: data["netin"],
277+
value_fn=lambda data: data.get("netin"),
263278
device_class=SensorDeviceClass.DATA_SIZE,
264279
native_unit_of_measurement=UnitOfInformation.BYTES,
265280
suggested_unit_of_measurement=UnitOfInformation.GIBIBYTES,
@@ -270,7 +285,7 @@ class ProxmoxStorageSensorEntityDescription(SensorEntityDescription):
270285
ProxmoxVMSensorEntityDescription(
271286
key="vm_netout",
272287
translation_key="vm_netout",
273-
value_fn=lambda data: data["netout"],
288+
value_fn=lambda data: data.get("netout"),
274289
device_class=SensorDeviceClass.DATA_SIZE,
275290
native_unit_of_measurement=UnitOfInformation.BYTES,
276291
suggested_unit_of_measurement=UnitOfInformation.GIBIBYTES,
@@ -284,12 +299,14 @@ class ProxmoxStorageSensorEntityDescription(SensorEntityDescription):
284299
ProxmoxContainerSensorEntityDescription(
285300
key="container_max_cpu",
286301
translation_key="container_max_cpu",
287-
value_fn=lambda data: data["cpus"],
302+
value_fn=lambda data: data.get("cpus"),
288303
),
289304
ProxmoxContainerSensorEntityDescription(
290305
key="container_cpu",
291306
translation_key="container_cpu",
292-
value_fn=lambda data: data["cpu"] * 100,
307+
value_fn=lambda data: (
308+
value * 100 if (value := data.get("cpu")) is not None else None
309+
),
293310
native_unit_of_measurement=PERCENTAGE,
294311
entity_category=EntityCategory.DIAGNOSTIC,
295312
suggested_display_precision=2,
@@ -298,7 +315,7 @@ class ProxmoxStorageSensorEntityDescription(SensorEntityDescription):
298315
ProxmoxContainerSensorEntityDescription(
299316
key="container_memory",
300317
translation_key="container_memory",
301-
value_fn=lambda data: data["mem"],
318+
value_fn=lambda data: data.get("mem"),
302319
device_class=SensorDeviceClass.DATA_SIZE,
303320
native_unit_of_measurement=UnitOfInformation.BYTES,
304321
suggested_unit_of_measurement=UnitOfInformation.GIBIBYTES,
@@ -309,7 +326,7 @@ class ProxmoxStorageSensorEntityDescription(SensorEntityDescription):
309326
ProxmoxContainerSensorEntityDescription(
310327
key="container_max_memory",
311328
translation_key="container_max_memory",
312-
value_fn=lambda data: data["maxmem"],
329+
value_fn=lambda data: data.get("maxmem"),
313330
device_class=SensorDeviceClass.DATA_SIZE,
314331
native_unit_of_measurement=UnitOfInformation.BYTES,
315332
suggested_unit_of_measurement=UnitOfInformation.GIBIBYTES,
@@ -320,7 +337,11 @@ class ProxmoxStorageSensorEntityDescription(SensorEntityDescription):
320337
ProxmoxContainerSensorEntityDescription(
321338
key="container_memory_percentage",
322339
translation_key="container_memory_percentage",
323-
value_fn=lambda data: int(data["mem"]) / int(data["maxmem"]) * 100,
340+
value_fn=lambda data: (
341+
int(mem) / int(maxmem) * 100
342+
if (mem := data.get("mem")) is not None and (maxmem := data.get("maxmem"))
343+
else None
344+
),
324345
native_unit_of_measurement=PERCENTAGE,
325346
entity_category=EntityCategory.DIAGNOSTIC,
326347
suggested_display_precision=2,
@@ -329,7 +350,7 @@ class ProxmoxStorageSensorEntityDescription(SensorEntityDescription):
329350
ProxmoxContainerSensorEntityDescription(
330351
key="container_uptime",
331352
translation_key="container_uptime",
332-
value_fn=lambda data: data["uptime"],
353+
value_fn=lambda data: data.get("uptime"),
333354
device_class=SensorDeviceClass.DURATION,
334355
native_unit_of_measurement=UnitOfTime.SECONDS,
335356
suggested_unit_of_measurement=UnitOfTime.HOURS,
@@ -339,7 +360,7 @@ class ProxmoxStorageSensorEntityDescription(SensorEntityDescription):
339360
ProxmoxContainerSensorEntityDescription(
340361
key="container_disk",
341362
translation_key="container_disk",
342-
value_fn=lambda data: data["disk"],
363+
value_fn=lambda data: data.get("disk"),
343364
device_class=SensorDeviceClass.DATA_SIZE,
344365
native_unit_of_measurement=UnitOfInformation.BYTES,
345366
suggested_unit_of_measurement=UnitOfInformation.GIBIBYTES,
@@ -350,7 +371,7 @@ class ProxmoxStorageSensorEntityDescription(SensorEntityDescription):
350371
ProxmoxContainerSensorEntityDescription(
351372
key="container_max_disk",
352373
translation_key="container_max_disk",
353-
value_fn=lambda data: data["maxdisk"],
374+
value_fn=lambda data: data.get("maxdisk"),
354375
device_class=SensorDeviceClass.DATA_SIZE,
355376
native_unit_of_measurement=UnitOfInformation.BYTES,
356377
suggested_unit_of_measurement=UnitOfInformation.GIBIBYTES,
@@ -361,14 +382,14 @@ class ProxmoxStorageSensorEntityDescription(SensorEntityDescription):
361382
ProxmoxContainerSensorEntityDescription(
362383
key="container_status",
363384
translation_key="container_status",
364-
value_fn=lambda data: data["status"],
385+
value_fn=lambda data: data.get("status"),
365386
device_class=SensorDeviceClass.ENUM,
366387
options=["running", "stopped", "suspended"],
367388
),
368389
ProxmoxContainerSensorEntityDescription(
369390
key="container_netin",
370391
translation_key="container_netin",
371-
value_fn=lambda data: data["netin"],
392+
value_fn=lambda data: data.get("netin"),
372393
device_class=SensorDeviceClass.DATA_SIZE,
373394
native_unit_of_measurement=UnitOfInformation.BYTES,
374395
suggested_unit_of_measurement=UnitOfInformation.GIBIBYTES,
@@ -379,7 +400,7 @@ class ProxmoxStorageSensorEntityDescription(SensorEntityDescription):
379400
ProxmoxContainerSensorEntityDescription(
380401
key="container_netout",
381402
translation_key="container_netout",
382-
value_fn=lambda data: data["netout"],
403+
value_fn=lambda data: data.get("netout"),
383404
device_class=SensorDeviceClass.DATA_SIZE,
384405
native_unit_of_measurement=UnitOfInformation.BYTES,
385406
suggested_unit_of_measurement=UnitOfInformation.GIBIBYTES,
@@ -393,7 +414,7 @@ class ProxmoxStorageSensorEntityDescription(SensorEntityDescription):
393414
ProxmoxStorageSensorEntityDescription(
394415
key="storage_used",
395416
translation_key="storage_used",
396-
value_fn=lambda data: data["used"],
417+
value_fn=lambda data: data.get("used"),
397418
device_class=SensorDeviceClass.DATA_SIZE,
398419
native_unit_of_measurement=UnitOfInformation.BYTES,
399420
suggested_unit_of_measurement=UnitOfInformation.GIBIBYTES,
@@ -404,7 +425,7 @@ class ProxmoxStorageSensorEntityDescription(SensorEntityDescription):
404425
ProxmoxStorageSensorEntityDescription(
405426
key="storage_total",
406427
translation_key="storage_total",
407-
value_fn=lambda data: data["total"],
428+
value_fn=lambda data: data.get("total"),
408429
device_class=SensorDeviceClass.DATA_SIZE,
409430
native_unit_of_measurement=UnitOfInformation.BYTES,
410431
suggested_unit_of_measurement=UnitOfInformation.GIBIBYTES,
@@ -415,7 +436,7 @@ class ProxmoxStorageSensorEntityDescription(SensorEntityDescription):
415436
ProxmoxStorageSensorEntityDescription(
416437
key="storage_available",
417438
translation_key="storage_available",
418-
value_fn=lambda data: data["avail"],
439+
value_fn=lambda data: data.get("avail"),
419440
device_class=SensorDeviceClass.DATA_SIZE,
420441
native_unit_of_measurement=UnitOfInformation.BYTES,
421442
suggested_unit_of_measurement=UnitOfInformation.GIBIBYTES,

0 commit comments

Comments
 (0)