Skip to content

Commit

Permalink
camera.py entity.name and entity unique_id correctly assigned.
Browse files Browse the repository at this point in the history
config_flow.py changed title with the defined camera name.
const.py changed att to attr to follow HA standards
connector.py modified Logging.
manifest.json updated version number.
Signed-off-by: SCA075 <[email protected]>

Signed-off-by: SCA075 <[email protected]>
  • Loading branch information
sca075 committed Aug 13, 2023
1 parent 7ffc870 commit f0b320c
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 86 deletions.
52 changes: 29 additions & 23 deletions custom_components/valetudo_vacuum_camera/camera.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Camera Version 1.2.0"""
"""Camera Version 1.2.1"""
from __future__ import annotations
import logging
import os
Expand Down Expand Up @@ -41,8 +41,8 @@
DEFAULT_NAME,
DOMAIN,
PLATFORMS,
ATT_ROTATE,
ATT_CROP,
ATTR_ROTATE,
ATTR_CROP,
COLOR_WALL,
COLOR_ZONE_CLEAN,
COLOR_ROBOT,
Expand Down Expand Up @@ -75,8 +75,8 @@
vol.Required(CONF_VACUUM_ENTITY_ID): cv.string,
vol.Required(CONF_MQTT_USER): cv.string,
vol.Required(CONF_MQTT_PASS): cv.string,
vol.Required(ATT_ROTATE, default="0"): cv.string,
vol.Required(ATT_CROP, default="50"): cv.string,
vol.Required(ATTR_ROTATE, default="0"): cv.string,
vol.Required(ATTR_CROP, default="50"): cv.string,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.entity_id,
}
)
Expand Down Expand Up @@ -109,17 +109,19 @@ async def async_setup_platform(


class ValetudoCamera(Camera, Entity):
_attr_has_entity_name = True

def __init__(self, hass, device_info):
super().__init__()
self.hass = hass
self._name = device_info.get(CONF_NAME)
self._attr_unique_id = "_" # uses the config name for unique id
self._vacuum_entity = device_info.get(CONF_VACUUM_ENTITY_ID)
self._mqtt_listen_topic = device_info.get(CONF_VACUUM_CONNECTION_STRING)
if self._mqtt_listen_topic:
self._mqtt_listen_topic = str(self._mqtt_listen_topic)
file_name = self._mqtt_listen_topic.split("/")
self.snapshot_img = "/config/www/snapshot_" + file_name[1].lower() + ".png"
self._attr_name = file_name[1] + " Camera"
self._attr_unique_id = file_name[1].lower() + "_camera"
self.file_name = file_name[1].lower()
self._mqtt_user = device_info.get(CONF_MQTT_USER)
self._mqtt_pass = device_info.get(CONF_MQTT_PASS)
Expand All @@ -138,14 +140,14 @@ def __init__(self, hass, device_info):
self._base = None
self._current = None
self._temp_dir = "config/tmp"
self._image_rotate = device_info.get(ATT_ROTATE)
self._image_rotate = device_info.get(ATTR_ROTATE)
if self._image_rotate:
self._image_rotate = int(device_info.get(ATT_ROTATE))
self._image_rotate = int(device_info.get(ATTR_ROTATE))
else:
self._image_rotate = 0
self._image_crop = device_info.get(ATT_CROP)
self._image_crop = device_info.get(ATTR_CROP)
if self._image_crop:
self._image_crop = int(device_info.get(ATT_CROP))
self._image_crop = int(device_info.get(ATTR_CROP))
else:
self._image_crop = 0
self._image = self.update()
Expand Down Expand Up @@ -214,7 +216,7 @@ def camera_image(

@property
def name(self) -> str:
return self._name
return self._attr_name

def turn_on(self):
self._mqtt.client_start()
Expand Down Expand Up @@ -263,14 +265,14 @@ def empty_if_no_data(self):
if os.path.isfile(self.snapshot_img) and (self._last_image is None):
# Load the snapshot image
self._last_image = Image.open(self.snapshot_img)
_LOGGER.info("Snapshot image loaded")
_LOGGER.info(self.file_name + ": Snapshot image loaded")
return self._last_image
elif self._last_image is not None:
return self._last_image
else:
# Create an empty image with a gray background
empty_img = Image.new("RGB", (800, 600), "gray")
_LOGGER.info("Staring up ...")
_LOGGER.info(self.file_name + ": Staring up ...")
return empty_img

def take_snapshot(self, json_data, image_data):
Expand All @@ -292,15 +294,16 @@ def take_snapshot(self, json_data, image_data):
image_data.save(
self.snapshot_img
)
_LOGGER.info("Camera Snapshot Taken.")
_LOGGER.info(self.file_name + ": Camera Snapshot Taken.")
except IOError:
self._snapshot_taken = None
_LOGGER.warning(
"Error Saving Image Snapshot, no snapshot available till restart."
"Error Saving" + self.file_name + ": Snapshot, no snapshot available till restart."
)
else:
_LOGGER.debug(
"Snapshot acquired during %s",
self.file_name +
": Snapshot acquired during %s",
{self._vacuum_state},
" Vacuum State.",
)
Expand All @@ -325,7 +328,7 @@ def update(self):
# take the snapshot.
self._snapshot_taken = False
# Starting the image processing.
_LOGGER.info("Camera image data update available: %s", process_data)
_LOGGER.info(self.file_name + ": Camera image data update available: %s", process_data)
start_time = datetime.now()
try:
# bypassed code is for debug purpose only
Expand All @@ -350,11 +353,13 @@ def update(self):
self._image_crop,
self._vacuum_shared.get_user_colors(),
self._vacuum_shared.get_rooms_colors(),
self.file_name,
)
if pil_img is not None:
pil_img = pil_img.rotate(self._image_rotate)
_LOGGER.debug(
"Applied image rotation: %s", {self._image_rotate}
"Applied " + self.file_name + " image rotation: %s",
{self._image_rotate}
)
if not self._snapshot_taken and (
self._vacuum_state == "idle"
Expand All @@ -367,7 +372,8 @@ def update(self):
is not self._map_handler.get_frame_number()
):
self._image_grab = False
_LOGGER.info("Suspended the camera data processing.")
_LOGGER.info("Suspended the camera data processing for: " +
self.file_name + ".")
# take a snapshot
self.take_snapshot(parsed_json, pil_img)
self._vac_json_id = self._map_handler.get_json_id()
Expand All @@ -391,13 +397,13 @@ def update(self):
self._image = bytes_data
# clean up
del buffered, pil_img, bytes_data
_LOGGER.info("Camera image update complete")
_LOGGER.info(self.file_name + ": Image update complete")
processing_time = (datetime.now() - start_time).total_seconds()
self._frame_interval = max(0.1, processing_time)
_LOGGER.debug("Adjusted frame interval: %s", self._frame_interval)
_LOGGER.debug("Adjusted " + self.file_name + ": Frame interval: %s", self._frame_interval)
else:
_LOGGER.info(
"Camera image not processed. Returning not updated image."
self.file_name + ": Image not processed. Returning not updated image."
)
self._frame_interval = 0.1
return self._image
47 changes: 26 additions & 21 deletions custom_components/valetudo_vacuum_camera/config_flow.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
"""config_flow ver.1.1.9"""
"""config_flow ver.1.2.1"""

import voluptuous as vol
import logging
from typing import Any, Dict, Optional
from homeassistant import config_entries

# from homeassistant.const import CONF_NAME
from homeassistant.core import callback
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.selector import EntitySelector, ColorRGBSelector
Expand All @@ -15,9 +14,8 @@
CONF_VACUUM_CONNECTION_STRING,
CONF_MQTT_USER,
CONF_MQTT_PASS,
DEFAULT_NAME,
ATT_ROTATE,
ATT_CROP,
ATTR_ROTATE,
ATTR_CROP,
COLOR_MOVE,
COLOR_ROBOT,
COLOR_WALL,
Expand Down Expand Up @@ -63,8 +61,8 @@

IMG_SCHEMA = vol.Schema(
{
vol.Required(ATT_ROTATE, default="0"): vol.In(["0", "90", "180", "270"]),
vol.Required(ATT_CROP, default="50"): cv.string,
vol.Required(ATTR_ROTATE, default="0"): vol.In(["0", "90", "180", "270"]),
vol.Required(ATTR_CROP, default="50"): cv.string,
}
)

Expand Down Expand Up @@ -106,6 +104,9 @@
class ValetudoCameraFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
VERSION = 1.2

def __init__(self):
self.data = None

async def async_step_user(self, user_input: Optional[Dict[str, Any]] = None):
if user_input is not None:
return await self.async_step_mqtt()
Expand All @@ -123,8 +124,8 @@ async def async_step_options_1(self, user_input: Optional[Dict[str, Any]] = None
if user_input is not None:
self.data.update(
{
"rotate_image": user_input.get(ATT_ROTATE),
"crop_image": user_input.get(ATT_CROP),
"rotate_image": user_input.get(ATTR_ROTATE),
"crop_image": user_input.get(ATTR_CROP),
}
)

Expand Down Expand Up @@ -192,16 +193,20 @@ async def async_step_options_3(self, user_input: Optional[Dict[str, Any]] = None
}
)

tmp_name = self.data["vacuum_map"]
tmp_name = tmp_name.split("/")
default_name = tmp_name[1] + " Camera"

return self.async_create_entry(
title=DEFAULT_NAME,
title=default_name,
data=self.data,
)

return self.async_show_form(
step_id="options_3",
data_schema=ROOMS_COLOR_SCHEMA,
description_placeholders=self.data,
)
step_id="options_3",
data_schema=ROOMS_COLOR_SCHEMA,
description_placeholders=self.data,
)

@staticmethod
@callback
Expand All @@ -222,10 +227,10 @@ def __init__(self, config_entry: config_entries.ConfigEntry):
self.IMG_SCHEMA = vol.Schema(
{
vol.Required(
ATT_ROTATE, default=config_entry.options.get("rotate_image")
ATTR_ROTATE, default=config_entry.options.get("rotate_image")
): vol.In(["0", "90", "180", "270"]),
vol.Required(
ATT_CROP, default=config_entry.options.get("crop_image")
ATTR_CROP, default=config_entry.options.get("crop_image")
): cv.string,
}
)
Expand Down Expand Up @@ -315,10 +320,10 @@ def __init__(self, config_entry: config_entries.ConfigEntry):
self.IMG_SCHEMA = vol.Schema(
{
vol.Required(
ATT_ROTATE, default=config_entry.data.get("rotate_image")
ATTR_ROTATE, default=config_entry.data.get("rotate_image")
): vol.In(["0", "90", "180", "270"]),
vol.Required(
ATT_CROP, default=config_entry.data.get("crop_image")
ATTR_CROP, default=config_entry.data.get("crop_image")
): cv.string,
}
)
Expand Down Expand Up @@ -352,7 +357,7 @@ def __init__(self, config_entry: config_entries.ConfigEntry):
): ColorRGBSelector(),
}
)
self.COLOR_2_SCHEMA =vol.Schema(
self.COLOR_2_SCHEMA = vol.Schema(
{
vol.Optional(
COLOR_ROOM_0, default=config_entry.data.get("color_room_0")
Expand Down Expand Up @@ -412,8 +417,8 @@ async def async_step_init(self, user_input: Optional[Dict[str, Any]] = None):
if user_input is not None:
self.data.update(
{
"rotate_image": user_input.get(ATT_ROTATE),
"crop_image": user_input.get(ATT_CROP),
"rotate_image": user_input.get(ATTR_ROTATE),
"crop_image": user_input.get(ATTR_CROP),
}
)
return await self.async_step_init_2()
Expand Down
6 changes: 3 additions & 3 deletions custom_components/valetudo_vacuum_camera/const.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
"""Constants for the valetudo_vacuum_camera integration."""
"""Version 1.1.8"""
"""Version 1.2.1"""

"""Required in Config_Flow"""
PLATFORMS = ["camera"]
DOMAIN = "valetudo_vacuum_camera"
DEFAULT_NAME = "valetudo vacuum camera"
ATT_ROTATE = "rotate_image"
ATT_CROP = "crop_image"
ATTR_ROTATE = "rotate_image"
ATTR_CROP = "crop_image"
CONF_MQTT_PASS = "broker_password"
CONF_MQTT_USER = "broker_user"
CONF_VACUUM_CONNECTION_STRING = "vacuum_map"
Expand Down
2 changes: 1 addition & 1 deletion custom_components/valetudo_vacuum_camera/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@
"pillow",
"numpy"
],
"version": "v1.2.0"
"version": "v1.3.0-beta.1"
}
14 changes: 7 additions & 7 deletions custom_components/valetudo_vacuum_camera/utils/valetudo_jdata.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Version 1.1.5"""
"""Version 1.2.1"""
import logging
import struct
import zlib
Expand Down Expand Up @@ -99,24 +99,24 @@ def extract_png_chunks(self, data):
_LOGGER.debug("Valetudo Json data grabbed")
return self._jdata

def camera_message_received(self, payload):
def camera_message_received(self, payload, source: "" = None):
# Process the camera data here
_LOGGER.debug("Decoding PNG to JSON")
_LOGGER.debug("Decoding, " + source + " PNG to JSON")
if payload is not None:
try:
extract_data = self.extract_png_chunks(payload)
except Warning as warning:
_LOGGER.warning("MQTT message format error:", {warning})
_LOGGER.warning(source + ": MQTT message format error:", {warning})
return None
else:
if self._jdata or extract_data is not None:
_LOGGER.debug("Extracting JSON")
_LOGGER.debug(source + ": Extracting JSON")
dec_data = zlib.decompress(self._jdata).decode("utf-8")
json_data = dec_data
response = json.loads(json_data)
_LOGGER.debug("Extracting JSON Complete")
_LOGGER.debug(source + ": Extracting JSON Complete")
del json_data
return response
else:
_LOGGER.debug("No data to process")
_LOGGER.debug(source + ": No data to process")
return None
Loading

0 comments on commit f0b320c

Please sign in to comment.