Skip to content

Commit c7aadcd

Browse files
authored
Add file name/size sensors to OctoPrint integration (home-assistant#148636)
1 parent 8256401 commit c7aadcd

File tree

4 files changed

+137
-23
lines changed

4 files changed

+137
-23
lines changed

homeassistant/components/octoprint/sensor.py

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
SensorStateClass,
1414
)
1515
from homeassistant.config_entries import ConfigEntry
16-
from homeassistant.const import PERCENTAGE, UnitOfTemperature
16+
from homeassistant.const import PERCENTAGE, UnitOfInformation, UnitOfTemperature
1717
from homeassistant.core import HomeAssistant, callback
1818
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
1919
from homeassistant.helpers.update_coordinator import CoordinatorEntity
@@ -84,6 +84,8 @@ def async_add_tool_sensors() -> None:
8484
OctoPrintJobPercentageSensor(coordinator, device_id),
8585
OctoPrintEstimatedFinishTimeSensor(coordinator, device_id),
8686
OctoPrintStartTimeSensor(coordinator, device_id),
87+
OctoPrintFileNameSensor(coordinator, device_id),
88+
OctoPrintFileSizeSensor(coordinator, device_id),
8789
]
8890

8991
async_add_entities(entities)
@@ -262,3 +264,61 @@ def native_value(self):
262264
def available(self) -> bool:
263265
"""Return if entity is available."""
264266
return self.coordinator.last_update_success and self.coordinator.data["printer"]
267+
268+
269+
class OctoPrintFileNameSensor(OctoPrintSensorBase):
270+
"""Representation of an OctoPrint sensor."""
271+
272+
def __init__(
273+
self,
274+
coordinator: OctoprintDataUpdateCoordinator,
275+
device_id: str,
276+
) -> None:
277+
"""Initialize a new OctoPrint sensor."""
278+
super().__init__(coordinator, "Current File", device_id)
279+
280+
@property
281+
def native_value(self) -> str | None:
282+
"""Return sensor state."""
283+
job: OctoprintJobInfo = self.coordinator.data["job"]
284+
285+
return job.job.file.name or None
286+
287+
@property
288+
def available(self) -> bool:
289+
"""Return if entity is available."""
290+
if not self.coordinator.last_update_success:
291+
return False
292+
job: OctoprintJobInfo = self.coordinator.data["job"]
293+
return job and job.job.file.name
294+
295+
296+
class OctoPrintFileSizeSensor(OctoPrintSensorBase):
297+
"""Representation of an OctoPrint sensor."""
298+
299+
_attr_device_class = SensorDeviceClass.DATA_SIZE
300+
_attr_native_unit_of_measurement = UnitOfInformation.BYTES
301+
_attr_suggested_unit_of_measurement = UnitOfInformation.MEGABYTES
302+
303+
def __init__(
304+
self,
305+
coordinator: OctoprintDataUpdateCoordinator,
306+
device_id: str,
307+
) -> None:
308+
"""Initialize a new OctoPrint sensor."""
309+
super().__init__(coordinator, "Current File Size", device_id)
310+
311+
@property
312+
def native_value(self) -> int | None:
313+
"""Return sensor state."""
314+
job: OctoprintJobInfo = self.coordinator.data["job"]
315+
316+
return job.job.file.size or None
317+
318+
@property
319+
def available(self) -> bool:
320+
"""Return if entity is available."""
321+
if not self.coordinator.last_update_success:
322+
return False
323+
job: OctoprintJobInfo = self.coordinator.data["job"]
324+
return job and job.job.file.size

tests/components/octoprint/__init__.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,21 @@
2121
from tests.common import MockConfigEntry
2222

2323
DEFAULT_JOB = {
24-
"job": {"file": {}},
24+
"job": {
25+
"averagePrintTime": None,
26+
"estimatedPrintTime": None,
27+
"filament": None,
28+
"file": {
29+
"date": None,
30+
"display": None,
31+
"name": None,
32+
"origin": None,
33+
"path": None,
34+
"size": None,
35+
},
36+
"lastPrintTime": None,
37+
"user": None,
38+
},
2539
"progress": {"completion": 50},
2640
}
2741

tests/components/octoprint/test_sensor.py

Lines changed: 61 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
from freezegun.api import FrozenDateTimeFactory
66

7+
from homeassistant.const import STATE_UNAVAILABLE, STATE_UNKNOWN, UnitOfInformation
78
from homeassistant.core import HomeAssistant
89
from homeassistant.helpers import entity_registry as er
910

@@ -23,11 +24,7 @@ async def test_sensors(
2324
},
2425
"temperature": {"tool1": {"actual": 18.83136, "target": 37.83136}},
2526
}
26-
job = {
27-
"job": {"file": {}},
28-
"progress": {"completion": 50, "printTime": 600, "printTimeLeft": 6000},
29-
"state": "Printing",
30-
}
27+
job = __standard_job()
3128
freezer.move_to(datetime(2020, 2, 20, 9, 10, 13, 543, tzinfo=UTC))
3229
await init_integration(hass, "sensor", printer=printer, job=job)
3330

@@ -80,6 +77,21 @@ async def test_sensors(
8077
entry = entity_registry.async_get("sensor.octoprint_estimated_finish_time")
8178
assert entry.unique_id == "Estimated Finish Time-uuid"
8279

80+
state = hass.states.get("sensor.octoprint_current_file")
81+
assert state is not None
82+
assert state.state == "Test_File_Name.gcode"
83+
assert state.name == "OctoPrint Current File"
84+
entry = entity_registry.async_get("sensor.octoprint_current_file")
85+
assert entry.unique_id == "Current File-uuid"
86+
87+
state = hass.states.get("sensor.octoprint_current_file_size")
88+
assert state is not None
89+
assert state.state == "123.456789"
90+
assert state.attributes.get("unit_of_measurement") == UnitOfInformation.MEGABYTES
91+
assert state.name == "OctoPrint Current File Size"
92+
entry = entity_registry.async_get("sensor.octoprint_current_file_size")
93+
assert entry.unique_id == "Current File Size-uuid"
94+
8395

8496
async def test_sensors_no_target_temp(
8597
hass: HomeAssistant,
@@ -106,11 +118,25 @@ async def test_sensors_no_target_temp(
106118

107119
state = hass.states.get("sensor.octoprint_target_tool1_temp")
108120
assert state is not None
109-
assert state.state == "unknown"
121+
assert state.state == STATE_UNKNOWN
110122
assert state.name == "OctoPrint target tool1 temp"
111123
entry = entity_registry.async_get("sensor.octoprint_target_tool1_temp")
112124
assert entry.unique_id == "target tool1 temp-uuid"
113125

126+
state = hass.states.get("sensor.octoprint_current_file")
127+
assert state is not None
128+
assert state.state == STATE_UNAVAILABLE
129+
assert state.name == "OctoPrint Current File"
130+
entry = entity_registry.async_get("sensor.octoprint_current_file")
131+
assert entry.unique_id == "Current File-uuid"
132+
133+
state = hass.states.get("sensor.octoprint_current_file_size")
134+
assert state is not None
135+
assert state.state == STATE_UNAVAILABLE
136+
assert state.name == "OctoPrint Current File Size"
137+
entry = entity_registry.async_get("sensor.octoprint_current_file_size")
138+
assert entry.unique_id == "Current File Size-uuid"
139+
114140

115141
async def test_sensors_paused(
116142
hass: HomeAssistant,
@@ -125,24 +151,20 @@ async def test_sensors_paused(
125151
},
126152
"temperature": {"tool1": {"actual": 18.83136, "target": None}},
127153
}
128-
job = {
129-
"job": {"file": {}},
130-
"progress": {"completion": 50, "printTime": 600, "printTimeLeft": 6000},
131-
"state": "Paused",
132-
}
154+
job = __standard_job()
133155
freezer.move_to(datetime(2020, 2, 20, 9, 10, 0))
134156
await init_integration(hass, "sensor", printer=printer, job=job)
135157

136158
state = hass.states.get("sensor.octoprint_start_time")
137159
assert state is not None
138-
assert state.state == "unknown"
160+
assert state.state == STATE_UNKNOWN
139161
assert state.name == "OctoPrint Start Time"
140162
entry = entity_registry.async_get("sensor.octoprint_start_time")
141163
assert entry.unique_id == "Start Time-uuid"
142164

143165
state = hass.states.get("sensor.octoprint_estimated_finish_time")
144166
assert state is not None
145-
assert state.state == "unknown"
167+
assert state.state == STATE_UNKNOWN
146168
assert state.name == "OctoPrint Estimated Finish Time"
147169
entry = entity_registry.async_get("sensor.octoprint_estimated_finish_time")
148170
assert entry.unique_id == "Estimated Finish Time-uuid"
@@ -154,11 +176,7 @@ async def test_sensors_printer_disconnected(
154176
entity_registry: er.EntityRegistry,
155177
) -> None:
156178
"""Test the underlying sensors."""
157-
job = {
158-
"job": {"file": {}},
159-
"progress": {"completion": 50, "printTime": 600, "printTimeLeft": 6000},
160-
"state": "Paused",
161-
}
179+
job = __standard_job()
162180
freezer.move_to(datetime(2020, 2, 20, 9, 10, 0))
163181
await init_integration(hass, "sensor", printer=None, job=job)
164182

@@ -171,21 +189,43 @@ async def test_sensors_printer_disconnected(
171189

172190
state = hass.states.get("sensor.octoprint_current_state")
173191
assert state is not None
174-
assert state.state == "unavailable"
192+
assert state.state == STATE_UNAVAILABLE
175193
assert state.name == "OctoPrint Current State"
176194
entry = entity_registry.async_get("sensor.octoprint_current_state")
177195
assert entry.unique_id == "Current State-uuid"
178196

179197
state = hass.states.get("sensor.octoprint_start_time")
180198
assert state is not None
181-
assert state.state == "unknown"
199+
assert state.state == STATE_UNKNOWN
182200
assert state.name == "OctoPrint Start Time"
183201
entry = entity_registry.async_get("sensor.octoprint_start_time")
184202
assert entry.unique_id == "Start Time-uuid"
185203

186204
state = hass.states.get("sensor.octoprint_estimated_finish_time")
187205
assert state is not None
188-
assert state.state == "unknown"
206+
assert state.state == STATE_UNKNOWN
189207
assert state.name == "OctoPrint Estimated Finish Time"
190208
entry = entity_registry.async_get("sensor.octoprint_estimated_finish_time")
191209
assert entry.unique_id == "Estimated Finish Time-uuid"
210+
211+
212+
def __standard_job():
213+
return {
214+
"job": {
215+
"averagePrintTime": 6500,
216+
"estimatedPrintTime": 6000,
217+
"filament": {"tool0": {"length": 3000, "volume": 7}},
218+
"file": {
219+
"date": 1577836800,
220+
"display": "Test File Name",
221+
"name": "Test_File_Name.gcode",
222+
"origin": "local",
223+
"path": "Folder1/Folder2/Test_File_Name.gcode",
224+
"size": 123456789,
225+
},
226+
"lastPrintTime": 12345.678,
227+
"user": "testUser",
228+
},
229+
"progress": {"completion": 50, "printTime": 600, "printTimeLeft": 6000},
230+
"state": "Printing",
231+
}
File renamed without changes.

0 commit comments

Comments
 (0)