Home Assistant Unofficial Reference 2024.12.1
image_processing.py
Go to the documentation of this file.
1 """Optical character recognition processing of seven segments displays."""
2 
3 from __future__ import annotations
4 
5 import io
6 import logging
7 import os
8 import subprocess
9 
10 from PIL import Image
11 import voluptuous as vol
12 
14  PLATFORM_SCHEMA as IMAGE_PROCESSING_PLATFORM_SCHEMA,
15  ImageProcessingDeviceClass,
16  ImageProcessingEntity,
17 )
18 from homeassistant.const import CONF_ENTITY_ID, CONF_NAME, CONF_SOURCE
19 from homeassistant.core import HomeAssistant, split_entity_id
21 from homeassistant.helpers.entity_platform import AddEntitiesCallback
22 from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
23 
24 _LOGGER = logging.getLogger(__name__)
25 
26 CONF_DIGITS = "digits"
27 CONF_EXTRA_ARGUMENTS = "extra_arguments"
28 CONF_HEIGHT = "height"
29 CONF_ROTATE = "rotate"
30 CONF_SSOCR_BIN = "ssocr_bin"
31 CONF_THRESHOLD = "threshold"
32 CONF_WIDTH = "width"
33 CONF_X_POS = "x_position"
34 CONF_Y_POS = "y_position"
35 
36 DEFAULT_BINARY = "ssocr"
37 
38 PLATFORM_SCHEMA = IMAGE_PROCESSING_PLATFORM_SCHEMA.extend(
39  {
40  vol.Optional(CONF_EXTRA_ARGUMENTS, default=""): cv.string,
41  vol.Optional(CONF_DIGITS): cv.positive_int,
42  vol.Optional(CONF_HEIGHT, default=0): cv.positive_int,
43  vol.Optional(CONF_SSOCR_BIN, default=DEFAULT_BINARY): cv.string,
44  vol.Optional(CONF_THRESHOLD, default=0): cv.positive_int,
45  vol.Optional(CONF_ROTATE, default=0): cv.positive_int,
46  vol.Optional(CONF_WIDTH, default=0): cv.positive_int,
47  vol.Optional(CONF_X_POS, default=0): cv.string,
48  vol.Optional(CONF_Y_POS, default=0): cv.positive_int,
49  }
50 )
51 
52 
54  hass: HomeAssistant,
55  config: ConfigType,
56  async_add_entities: AddEntitiesCallback,
57  discovery_info: DiscoveryInfoType | None = None,
58 ) -> None:
59  """Set up the Seven segments OCR platform."""
62  hass, camera[CONF_ENTITY_ID], config, camera.get(CONF_NAME)
63  )
64  for camera in config[CONF_SOURCE]
65  )
66 
67 
69  """Representation of the seven segments OCR image processing entity."""
70 
71  _attr_device_class = ImageProcessingDeviceClass.OCR
72 
73  def __init__(self, hass, camera_entity, config, name):
74  """Initialize seven segments processing."""
75  self.hasshasshass = hass
76  self._camera_entity_camera_entity = camera_entity
77  if name:
78  self._name_name = name
79  else:
80  self._name_name = f"SevenSegment OCR {split_entity_id(camera_entity)[1]}"
81  self._state_state = None
82 
83  self.filepathfilepath = os.path.join(
84  self.hasshasshass.config.config_dir,
85  f"ssocr-{self._name.replace(' ', '_')}.png",
86  )
87  crop = [
88  "crop",
89  str(config[CONF_X_POS]),
90  str(config[CONF_Y_POS]),
91  str(config[CONF_WIDTH]),
92  str(config[CONF_HEIGHT]),
93  ]
94  digits = ["-d", str(config.get(CONF_DIGITS, -1))]
95  rotate = ["rotate", str(config[CONF_ROTATE])]
96  threshold = ["-t", str(config[CONF_THRESHOLD])]
97  extra_arguments = config[CONF_EXTRA_ARGUMENTS].split(" ")
98 
99  self._command_command = [
100  config[CONF_SSOCR_BIN],
101  *crop,
102  *digits,
103  *threshold,
104  *rotate,
105  *extra_arguments,
106  ]
107  self._command_command.append(self.filepathfilepath)
108 
109  @property
110  def camera_entity(self):
111  """Return camera entity id from process pictures."""
112  return self._camera_entity_camera_entity
113 
114  @property
115  def name(self):
116  """Return the name of the image processor."""
117  return self._name_name
118 
119  @property
120  def state(self):
121  """Return the state of the entity."""
122  return self._state_state
123 
124  def process_image(self, image):
125  """Process the image."""
126  stream = io.BytesIO(image)
127  img = Image.open(stream)
128  img.save(self.filepathfilepath, "png")
129 
130  with subprocess.Popen(
131  self._command_command,
132  stdout=subprocess.PIPE,
133  stderr=subprocess.PIPE,
134  close_fds=False, # Required for posix_spawn
135  ) as ocr:
136  out = ocr.communicate()
137  if out[0] != b"":
138  self._state_state = out[0].strip().decode("utf-8")
139  else:
140  self._state_state = None
141  _LOGGER.warning(
142  "Unable to detect value: %s", out[1].strip().decode("utf-8")
143  )
None async_setup_platform(HomeAssistant hass, ConfigType config, AddEntitiesCallback async_add_entities, DiscoveryInfoType|None discovery_info=None)