From 96d81c2bb7c07e19a92258660967cf79588c9813 Mon Sep 17 00:00:00 2001 From: Helge Date: Sun, 24 Apr 2022 21:27:08 +0200 Subject: [PATCH 1/9] fix Crash when no contact to MQTT server #258 --- picframe/interface_mqtt.py | 8 +++++--- picframe/start.py | 10 ++++++++-- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/picframe/interface_mqtt.py b/picframe/interface_mqtt.py index 10886ab..eaff839 100644 --- a/picframe/interface_mqtt.py +++ b/picframe/interface_mqtt.py @@ -46,21 +46,23 @@ def __init__(self, controller, mqtt_config): self.__device_id = mqtt_config['device_id'] self.__device_url = mqtt_config['device_url'] except Exception as e: - self.__logger.info("MQTT not set up because of: {}".format(e)) + self.__logger.error("MQTT not set up because of: {}".format(e)) + raise def start(self): try: self.__controller.publish_state = self.publish_state self.__client.loop_start() except Exception as e: - self.__logger.info("MQTT not started because of: {}".format(e)) + self.__logger.error("MQTT not started because of: {}".format(e)) + raise def stop(self): try: self.__controller.publish_state = None self.__client.loop_stop() except Exception as e: - self.__logger.info("MQTT stopping failed because of: {}".format(e)) + self.__logger.error("MQTT stopping failed because of: {}".format(e)) def on_connect(self, client, userdata, flags, rc): diff --git a/picframe/start.py b/picframe/start.py index 8926e48..fe7ab87 100644 --- a/picframe/start.py +++ b/picframe/start.py @@ -138,8 +138,14 @@ def main(): mqtt_config = m.get_mqtt_config() if mqtt_config['use_mqtt']: from picframe import interface_mqtt - mqtt = interface_mqtt.InterfaceMQTT(c, mqtt_config) - mqtt.start() + try: + mqtt = interface_mqtt.InterfaceMQTT(c, mqtt_config) + mqtt.start() + except: + logger.error("Can't initialize mqtt. Stopping picframe") + sys.exit(1) + + http_config = m.get_http_config() model_config = m.get_model_config() From 6a2cdd57f92e74f9bf17b022f19362293e7d3906 Mon Sep 17 00:00:00 2001 From: helgeerbe Date: Fri, 13 May 2022 15:17:08 +0200 Subject: [PATCH 2/9] Separate image from config notification --- picframe/interface_mqtt.py | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/picframe/interface_mqtt.py b/picframe/interface_mqtt.py index eaff839..1b0d87a 100644 --- a/picframe/interface_mqtt.py +++ b/picframe/interface_mqtt.py @@ -72,6 +72,7 @@ def on_connect(self, client, userdata, flags, rc): self.__logger.info('Connected with mqtt broker') sensor_topic_head = "homeassistant/sensor/" + self.__device_id + image_topic_head = "homeassistant/image/" + self.__device_id number_topic_head = "homeassistant/number/" + self.__device_id select_topic_head = "homeassistant/select/" + self.__device_id switch_topic_head = "homeassistant/switch/" + self.__device_id @@ -86,7 +87,7 @@ def on_connect(self, client, userdata, flags, rc): self.__setup_sensor(client, sensor_topic_head, "location_filter", "mdi:map-search", available_topic, entity_category="config") self.__setup_sensor(client, sensor_topic_head, "tags_filter", "mdi:image-search", available_topic, entity_category="config") self.__setup_sensor(client, sensor_topic_head, "image_counter", "mdi:camera-burst", available_topic, entity_category="diagnostic") - self.__setup_sensor(client, sensor_topic_head, "image", "mdi:file-image", available_topic, has_attributes=True, entity_category="diagnostic") + self.__setup_sensor(client, image_topic_head, "image", "mdi:file-image", available_topic, has_attributes=True, entity_category="diagnostic") ## numbers self.__setup_number(client, number_topic_head, "brightness", 0.0, 1.0, 0.1, "mdi:brightness-6", available_topic) @@ -383,13 +384,28 @@ def on_message(self, client, userdata, message): elif message.topic == self.__device_id + "/stop": self.__controller.stop() - def publish_state(self, image, image_attr): + def publish_state(self, image=None, image_attr=None): sensor_topic_head = "homeassistant/sensor/" + self.__device_id + image_topic_head = "homeassistant/image/" + self.__device_id switch_topic_head = "homeassistant/switch/" + self.__device_id select_topic_head = "homeassistant/select/" + self.__device_id sensor_state_topic = sensor_topic_head + "/state" + image_state_topic = image_topic_head + "/state" sensor_state_payload = {} + image_state_payload = {} + + ## image + # image attributes + if image_attr is not None: + attributes_topic = sensor_topic_head + "_image/attributes" + self.__logger.debug("Send image attributes: %s", image_attr) + self.__client.publish(attributes_topic, json.dumps(image_attr), qos=0, retain=False) + # image sensor + if image is not None: + _, tail = os.path.split(image) + image_state_payload["image"] = tail + self.__client.publish(image_state_topic, json.dumps(image_state_payload), qos=0, retain=False) ## sensor # directory sensor @@ -397,9 +413,6 @@ def publish_state(self, image, image_attr): sensor_state_payload["directory"] = actual_dir # image counter sensor sensor_state_payload["image_counter"] = str(self.__controller.get_number_of_files()) - # image sensor - _, tail = os.path.split(image) - sensor_state_payload["image"] = tail # date_from sensor_state_payload["date_from"] = int(self.__controller.date_from) # date_to @@ -408,7 +421,6 @@ def publish_state(self, image, image_attr): sensor_state_payload["location_filter"] = self.__controller.location_filter # tags_filter sensor_state_payload["tags_filter"] = self.__controller.tags_filter - ## number state # time_delay sensor_state_payload["time_delay"] = self.__controller.time_delay @@ -419,17 +431,14 @@ def publish_state(self, image, image_attr): # matting_images sensor_state_payload["matting_images"] = self.__controller.matting_images - # send last will and testament - available_topic = switch_topic_head + "/available" - self.__client.publish(available_topic, "online", qos=0, retain=True) - #pulish sensors - attributes_topic = sensor_topic_head + "_image/attributes" - self.__logger.debug("Send image attributes: %s", image_attr) - self.__client.publish(attributes_topic, json.dumps(image_attr), qos=0, retain=False) dir_list.sort() self.__setup_select(self.__client, select_topic_head, "directory", dir_list, "mdi:folder-multiple-image", available_topic, init=False) self.__logger.info("Send sensor state: %s", sensor_state_payload) self.__client.publish(sensor_state_topic, json.dumps(sensor_state_payload), qos=0, retain=False) + # send last will and testament + available_topic = switch_topic_head + "/available" + self.__client.publish(available_topic, "online", qos=0, retain=True) + From 6d8505b19b0521a96d9df0e9b38fef38cd1a551b Mon Sep 17 00:00:00 2001 From: Helge Date: Fri, 13 May 2022 20:48:38 +0200 Subject: [PATCH 3/9] fix and clean up mqtt split --- picframe/interface_mqtt.py | 88 +++++++++++++++++++------------------- 1 file changed, 43 insertions(+), 45 deletions(-) diff --git a/picframe/interface_mqtt.py b/picframe/interface_mqtt.py index 1b0d87a..32811c4 100644 --- a/picframe/interface_mqtt.py +++ b/picframe/interface_mqtt.py @@ -71,79 +71,76 @@ def on_connect(self, client, userdata, flags, rc): return self.__logger.info('Connected with mqtt broker') - sensor_topic_head = "homeassistant/sensor/" + self.__device_id - image_topic_head = "homeassistant/image/" + self.__device_id - number_topic_head = "homeassistant/number/" + self.__device_id - select_topic_head = "homeassistant/select/" + self.__device_id - switch_topic_head = "homeassistant/switch/" + self.__device_id - # send last will and testament - available_topic = switch_topic_head + "/available" + available_topic = "homeassistant/switch/" + self.__device_id + "/available" client.publish(available_topic, "online", qos=0, retain=True) ## sensors - self.__setup_sensor(client, sensor_topic_head, "date_from", "mdi:calendar-arrow-left", available_topic, entity_category="config") - self.__setup_sensor(client, sensor_topic_head, "date_to", "mdi:calendar-arrow-right", available_topic, entity_category="config") - self.__setup_sensor(client, sensor_topic_head, "location_filter", "mdi:map-search", available_topic, entity_category="config") - self.__setup_sensor(client, sensor_topic_head, "tags_filter", "mdi:image-search", available_topic, entity_category="config") - self.__setup_sensor(client, sensor_topic_head, "image_counter", "mdi:camera-burst", available_topic, entity_category="diagnostic") - self.__setup_sensor(client, image_topic_head, "image", "mdi:file-image", available_topic, has_attributes=True, entity_category="diagnostic") + self.__setup_sensor(client, "date_from", "mdi:calendar-arrow-left", available_topic, entity_category="config") + self.__setup_sensor(client, "date_to", "mdi:calendar-arrow-right", available_topic, entity_category="config") + self.__setup_sensor(client, "location_filter", "mdi:map-search", available_topic, entity_category="config") + self.__setup_sensor(client, "tags_filter", "mdi:image-search", available_topic, entity_category="config") + self.__setup_sensor(client, "image_counter", "mdi:camera-burst", available_topic, entity_category="diagnostic") + self.__setup_sensor(client, "image", "mdi:file-image", available_topic, has_attributes=True, entity_category="diagnostic") ## numbers - self.__setup_number(client, number_topic_head, "brightness", 0.0, 1.0, 0.1, "mdi:brightness-6", available_topic) - self.__setup_number(client, number_topic_head, "time_delay", 1, 400, 1, "mdi:image-plus", available_topic) - self.__setup_number(client, number_topic_head, "fade_time", 1, 50, 1,"mdi:image-size-select-large", available_topic) - self.__setup_number(client, number_topic_head, "matting_images", 0.0, 1.0, 0.01, "mdi:image-frame", available_topic) + self.__setup_number(client, "brightness", 0.0, 1.0, 0.1, "mdi:brightness-6", available_topic) + self.__setup_number(client, "time_delay", 1, 400, 1, "mdi:image-plus", available_topic) + self.__setup_number(client, "fade_time", 1, 50, 1,"mdi:image-size-select-large", available_topic) + self.__setup_number(client, "matting_images", 0.0, 1.0, 0.01, "mdi:image-frame", available_topic) ## selects _, dir_list = self.__controller.get_directory_list() dir_list.sort() - self.__setup_select(client, select_topic_head, "directory", dir_list, "mdi:folder-multiple-image", available_topic, init=True) + self.__setup_select(client, "directory", dir_list, "mdi:folder-multiple-image", available_topic, init=True) command_topic = self.__device_id + "/directory" client.subscribe(command_topic, qos=0) ## switches - self.__setup_switch(client, switch_topic_head, "_text_refresh", "mdi:refresh", available_topic, entity_category="config") - self.__setup_switch(client, switch_topic_head, "_delete", "mdi:delete", available_topic) - self.__setup_switch(client, switch_topic_head, "_name_toggle", "mdi:subtitles", available_topic, + self.__setup_switch(client, "_text_refresh", "mdi:refresh", available_topic, entity_category="config") + self.__setup_switch(client, "_delete", "mdi:delete", available_topic) + self.__setup_switch(client, "_name_toggle", "mdi:subtitles", available_topic, self.__controller.text_is_on("name"), entity_category="config") - self.__setup_switch(client, switch_topic_head, "_title_toggle", "mdi:subtitles", available_topic, + self.__setup_switch(client, "_title_toggle", "mdi:subtitles", available_topic, self.__controller.text_is_on("title"), entity_category="config") - self.__setup_switch(client, switch_topic_head, "_caption_toggle", "mdi:subtitles", available_topic, + self.__setup_switch(client, "_caption_toggle", "mdi:subtitles", available_topic, self.__controller.text_is_on("caption"), entity_category="config") - self.__setup_switch(client, switch_topic_head, "_date_toggle", "mdi:calendar-today", available_topic, + self.__setup_switch(client, "_date_toggle", "mdi:calendar-today", available_topic, self.__controller.text_is_on("date"), entity_category="config") - self.__setup_switch(client, switch_topic_head, "_location_toggle", "mdi:crosshairs-gps", available_topic, + self.__setup_switch(client, "_location_toggle", "mdi:crosshairs-gps", available_topic, self.__controller.text_is_on("location"), entity_category="config") - self.__setup_switch(client, switch_topic_head, "_directory_toggle", "mdi:folder", available_topic, + self.__setup_switch(client, "_directory_toggle", "mdi:folder", available_topic, self.__controller.text_is_on("directory"), entity_category="config") - self.__setup_switch(client, switch_topic_head, "_text_off", "mdi:badge-account-horizontal-outline", available_topic, entity_category="config") - self.__setup_switch(client, switch_topic_head, "_display", "mdi:panorama", available_topic, + self.__setup_switch(client, "_text_off", "mdi:badge-account-horizontal-outline", available_topic, entity_category="config") + self.__setup_switch(client, "_display", "mdi:panorama", available_topic, self.__controller.display_is_on) - self.__setup_switch(client, switch_topic_head, "_clock", "mdi:clock-outline", available_topic, + self.__setup_switch(client, "_clock", "mdi:clock-outline", available_topic, self.__controller.clock_is_on, entity_category="config") - self.__setup_switch(client, switch_topic_head, "_shuffle", "mdi:shuffle-variant", available_topic, + self.__setup_switch(client, "_shuffle", "mdi:shuffle-variant", available_topic, self.__controller.shuffle) - self.__setup_switch(client, switch_topic_head, "_paused", "mdi:pause", available_topic, + self.__setup_switch(client, "_paused", "mdi:pause", available_topic, self.__controller.paused) - self.__setup_switch(client, switch_topic_head, "_back", "mdi:skip-previous", available_topic) - self.__setup_switch(client, switch_topic_head, "_next", "mdi:skip-next", available_topic) + self.__setup_switch(client, "_back", "mdi:skip-previous", available_topic) + self.__setup_switch(client, "_next", "mdi:skip-next", available_topic) client.subscribe(self.__device_id + "/purge_files", qos=0) # close down without killing! client.subscribe(self.__device_id + "/stop", qos=0) # close down without killing! - def __setup_sensor(self, client, sensor_topic_head, topic, icon, available_topic, has_attributes=False, entity_category=None): + def __setup_sensor(self, client, topic, icon, available_topic, has_attributes=False, entity_category=None): + sensor_topic_head = "homeassistant/sensor/" + self.__device_id config_topic = sensor_topic_head + "_" + topic + "/config" name = self.__device_id + "_" + topic dict = {"name": name, "icon": icon, - "state_topic": sensor_topic_head + "/state", "value_template": "{{ value_json." + topic + "}}", "avty_t": available_topic, "uniq_id": name, "dev":{"ids":[self.__device_id]}} if has_attributes == True: + dict["state_topic"] = sensor_topic_head + "_" + topic + "/state" dict["json_attributes_topic"] = sensor_topic_head + "_" + topic + "/attributes" + else: + dict["state_topic"] = sensor_topic_head + "/state" if entity_category: dict["entity_category"] = entity_category @@ -151,7 +148,8 @@ def __setup_sensor(self, client, sensor_topic_head, topic, icon, available_topic client.publish(config_topic, config_payload, qos=0, retain=True) client.subscribe(self.__device_id + "/" + topic, qos=0) - def __setup_number(self, client, number_topic_head, topic, min, max, step, icon, available_topic): + def __setup_number(self, client, topic, min, max, step, icon, available_topic): + number_topic_head = "homeassistant/number/" + self.__device_id config_topic = number_topic_head + "_" + topic + "/config" command_topic = self.__device_id + "/" + topic state_topic = "homeassistant/sensor/" + self.__device_id + "/state" @@ -171,7 +169,8 @@ def __setup_number(self, client, number_topic_head, topic, min, max, step, icon, client.publish(config_topic, config_payload, qos=0, retain=True) client.subscribe(command_topic, qos=0) - def __setup_select(self, client, select_topic_head, topic, options, icon, available_topic, init=False): + def __setup_select(self, client, topic, options, icon, available_topic, init=False): + select_topic_head = "homeassistant/select/" + self.__device_id config_topic = select_topic_head + "_" + topic + "/config" command_topic = self.__device_id + "/" + topic state_topic = "homeassistant/sensor/" + self.__device_id + "/state" @@ -191,8 +190,9 @@ def __setup_select(self, client, select_topic_head, topic, options, icon, availa if init: client.subscribe(command_topic, qos=0) - def __setup_switch(self, client, switch_topic_head, topic, icon, + def __setup_switch(self, client, topic, icon, available_topic, is_on=False, entity_category=None): + switch_topic_head = "homeassistant/switch/" + self.__device_id config_topic = switch_topic_head + topic + "/config" command_topic = switch_topic_head + topic + "/set" state_topic = switch_topic_head + topic + "/state" @@ -386,11 +386,7 @@ def on_message(self, client, userdata, message): def publish_state(self, image=None, image_attr=None): sensor_topic_head = "homeassistant/sensor/" + self.__device_id - image_topic_head = "homeassistant/image/" + self.__device_id - switch_topic_head = "homeassistant/switch/" + self.__device_id - select_topic_head = "homeassistant/select/" + self.__device_id - sensor_state_topic = sensor_topic_head + "/state" - image_state_topic = image_topic_head + "/state" + available_topic = "homeassistant/switch/" + self.__device_id + "/available" sensor_state_payload = {} image_state_payload = {} @@ -405,6 +401,8 @@ def publish_state(self, image=None, image_attr=None): if image is not None: _, tail = os.path.split(image) image_state_payload["image"] = tail + image_state_topic = sensor_topic_head + "_image/state" + self.__logger.info("Send image state: %s", image_state_payload) self.__client.publish(image_state_topic, json.dumps(image_state_payload), qos=0, retain=False) ## sensor @@ -433,12 +431,12 @@ def publish_state(self, image=None, image_attr=None): #pulish sensors dir_list.sort() - self.__setup_select(self.__client, select_topic_head, "directory", dir_list, "mdi:folder-multiple-image", available_topic, init=False) + self.__setup_select(self.__client, "directory", dir_list, "mdi:folder-multiple-image", available_topic, init=False) self.__logger.info("Send sensor state: %s", sensor_state_payload) + sensor_state_topic = sensor_topic_head + "/state" self.__client.publish(sensor_state_topic, json.dumps(sensor_state_payload), qos=0, retain=False) # send last will and testament - available_topic = switch_topic_head + "/available" self.__client.publish(available_topic, "online", qos=0, retain=True) From 09301a246f58cdf7f783d3c09c542183f13b74ba Mon Sep 17 00:00:00 2001 From: Helge Date: Fri, 13 May 2022 22:00:33 +0200 Subject: [PATCH 4/9] - on delete image show next image - update brightness without next image --- picframe/controller.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/picframe/controller.py b/picframe/controller.py index 79aa79d..9b5e529 100644 --- a/picframe/controller.py +++ b/picframe/controller.py @@ -83,7 +83,7 @@ def back(self): def delete(self): self.__model.delete_file() - self.back() # TODO check needed to avoid skipping one as record has been deleted from model.__file_list + self.next() # TODO check needed to avoid skipping one as record has been deleted from model.__file_list self.__next_tm = 0 def set_show_text(self, txt_key=None, val="ON"): @@ -202,7 +202,7 @@ def brightness(self): @brightness.setter def brightness(self, val): self.__viewer.set_brightness(float(val)) - self.__next_tm = 0 + self.publish_state() @property def matting_images(self): From e1b17c11e1a40534f9acf5981964a552e7476fc9 Mon Sep 17 00:00:00 2001 From: Helge Date: Sat, 14 May 2022 18:56:41 +0200 Subject: [PATCH 5/9] Add notification for shuffle and display --- picframe/controller.py | 3 +++ picframe/interface_mqtt.py | 19 +++++++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/picframe/controller.py b/picframe/controller.py index 9b5e529..9ea6a6a 100644 --- a/picframe/controller.py +++ b/picframe/controller.py @@ -69,6 +69,7 @@ def paused(self, val:bool): self.__paused = val pic = self.__model.get_current_pics()[0] # only refresh left text self.__viewer.reset_name_tm(pic, val, side=0, pair=self.__model.get_current_pics()[1] is not None) + self.publish_state() def next(self): self.__next_tm = 0 @@ -154,6 +155,7 @@ def display_is_on(self): def display_is_on(self, on_off): self.paused = not on_off self.__viewer.display_is_on = on_off + self.publish_state() @property def clock_is_on(self): @@ -172,6 +174,7 @@ def shuffle(self, val:bool): self.__model.shuffle = val self.__model.force_reload() self.__next_tm = 0 + self.publish_state() @property def fade_time(self): diff --git a/picframe/interface_mqtt.py b/picframe/interface_mqtt.py index 32811c4..11d326a 100644 --- a/picframe/interface_mqtt.py +++ b/picframe/interface_mqtt.py @@ -385,8 +385,9 @@ def on_message(self, client, userdata, message): self.__controller.stop() def publish_state(self, image=None, image_attr=None): - sensor_topic_head = "homeassistant/sensor/" + self.__device_id - available_topic = "homeassistant/switch/" + self.__device_id + "/available" + sensor_topic_head = "homeassistant/sensor/" + self.__device_id + switch_topic_head = "homeassistant/switch/" + self.__device_id + available_topic = switch_topic_head + "/available" sensor_state_payload = {} image_state_payload = {} @@ -437,6 +438,20 @@ def publish_state(self, image=None, image_attr=None): sensor_state_topic = sensor_topic_head + "/state" self.__client.publish(sensor_state_topic, json.dumps(sensor_state_payload), qos=0, retain=False) + # publish state of switches + # pause + state_topic = switch_topic_head + "_paused/state" + payload = "ON" if self.__controller.paused else "OFF" + self.__client.publish(state_topic, payload, retain=True) + # shuffle + state_topic = switch_topic_head + "_shuffle/set" + payload = "ON" if self.__controller.shuffle else "OFF" + self.__client.publish(state_topic, payload, retain=True) + # display + state_topic = switch_topic_head + "_display/state" + payload = "ON" if self.__controller.display_is_on else "OFF" + self.__client.publish(state_topic, payload, retain=True) + # send last will and testament self.__client.publish(available_topic, "online", qos=0, retain=True) From f481b502752a885570a5a9918016fd20c5a3d6e7 Mon Sep 17 00:00:00 2001 From: Helge Date: Sat, 14 May 2022 21:20:15 +0200 Subject: [PATCH 6/9] back, next and delete are buttons now listens on homeassistant/button/... --- picframe/interface_mqtt.py | 49 ++++++++++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/picframe/interface_mqtt.py b/picframe/interface_mqtt.py index 11d326a..cd534fa 100644 --- a/picframe/interface_mqtt.py +++ b/picframe/interface_mqtt.py @@ -98,7 +98,6 @@ def on_connect(self, client, userdata, flags, rc): ## switches self.__setup_switch(client, "_text_refresh", "mdi:refresh", available_topic, entity_category="config") - self.__setup_switch(client, "_delete", "mdi:delete", available_topic) self.__setup_switch(client, "_name_toggle", "mdi:subtitles", available_topic, self.__controller.text_is_on("name"), entity_category="config") self.__setup_switch(client, "_title_toggle", "mdi:subtitles", available_topic, @@ -120,8 +119,11 @@ def on_connect(self, client, userdata, flags, rc): self.__controller.shuffle) self.__setup_switch(client, "_paused", "mdi:pause", available_topic, self.__controller.paused) - self.__setup_switch(client, "_back", "mdi:skip-previous", available_topic) - self.__setup_switch(client, "_next", "mdi:skip-next", available_topic) + + # buttons + self.__setup_button(client, "_delete", "mdi:delete", available_topic) + self.__setup_button(client, "_back", "mdi:skip-previous", available_topic) + self.__setup_button(client, "_next", "mdi:skip-next", available_topic) client.subscribe(self.__device_id + "/purge_files", qos=0) # close down without killing! client.subscribe(self.__device_id + "/stop", qos=0) # close down without killing! @@ -218,9 +220,36 @@ def __setup_switch(self, client, topic, icon, client.publish(config_topic, config_payload, qos=0, retain=True) client.publish(state_topic, "ON" if is_on else "OFF", qos=0, retain=True) + def __setup_button(self, client, topic, icon, + available_topic, entity_category=None): + button_topic_head = "homeassistant/button/" + self.__device_id + config_topic = button_topic_head + topic + "/config" + command_topic = button_topic_head + topic + "/set" + dict = {"name": self.__device_id + topic, + "icon": icon, + "command_topic": command_topic, + "payload_press": "ON", + "avty_t": available_topic, + "uniq_id": self.__device_id + topic, + "dev": { + "ids": [self.__device_id], + "name": self.__device_id, + "mdl": "PictureFrame", + "sw": __version__, + "mf": "pi3d PictureFrame project"}} + if self.__device_url : + dict["dev"]["cu"] = self.__device_url + if entity_category: + dict["entity_category"] = entity_category + config_payload = json.dumps(dict) + + client.subscribe(command_topic , qos=0) + client.publish(config_topic, config_payload, qos=0, retain=True) + def on_message(self, client, userdata, message): msg = message.payload.decode("utf-8") switch_topic_head = "homeassistant/switch/" + self.__device_id + button_topic_head = "homeassistant/button/" + self.__device_id ###### switches ###### # display @@ -260,22 +289,16 @@ def on_message(self, client, userdata, message): self.__controller.paused = False client.publish(state_topic, "OFF", retain=True) # back buttons - elif message.topic == switch_topic_head + "_back/set": - state_topic = switch_topic_head + "_back/state" + elif message.topic == button_topic_head + "_back/set": if msg == "ON": - client.publish(state_topic, "OFF", retain=True) self.__controller.back() # next buttons - elif message.topic == switch_topic_head + "_next/set": - state_topic = switch_topic_head + "_next/state" + elif message.topic == button_topic_head + "_next/set": if msg == "ON": - client.publish(state_topic, "OFF", retain=True) self.__controller.next() # delete - elif message.topic == switch_topic_head + "_delete/set": - state_topic = switch_topic_head + "_delete/state" + elif message.topic == button_topic_head + "_delete/set": if msg == "ON": - client.publish(state_topic, "OFF", retain=True) self.__controller.delete() # title on elif message.topic == switch_topic_head + "_title_toggle/set": @@ -444,7 +467,7 @@ def publish_state(self, image=None, image_attr=None): payload = "ON" if self.__controller.paused else "OFF" self.__client.publish(state_topic, payload, retain=True) # shuffle - state_topic = switch_topic_head + "_shuffle/set" + state_topic = switch_topic_head + "_shuffle/state" payload = "ON" if self.__controller.shuffle else "OFF" self.__client.publish(state_topic, payload, retain=True) # display From 61a5f0ace95942b6d44b55e4b59f2e77083cf74c Mon Sep 17 00:00:00 2001 From: Simon Trimmer Date: Fri, 22 Apr 2022 17:53:46 +0100 Subject: [PATCH 7/9] Add opacity option to text and clock --- picframe/config/configuration_example.yaml | 2 ++ picframe/model.py | 2 ++ picframe/viewer_display.py | 8 ++++++-- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/picframe/config/configuration_example.yaml b/picframe/config/configuration_example.yaml index f16f25a..514f054 100644 --- a/picframe/config/configuration_example.yaml +++ b/picframe/config/configuration_example.yaml @@ -14,6 +14,7 @@ viewer: show_text: "title caption name date folder location" # default="title caption name date folder location", show text, include combination of words: title, caption name, date, location, folder text_justify: "L" # text justification L, C or R text_bkg_hgt: 0.25 # default=0.25 (0.0-1.0), percentage of screen height for text background texture + text_opacity: 1.0 # default=1.0 (0.0-1.0), alpha value of text overlay fit: False # default=False, True => scale image so all visible and leave 'gaps' # False => crop image so no 'gaps' kenburns: False # default=False, will set fit->False and blur_edges->False @@ -38,6 +39,7 @@ viewer: clock_justify: "R" # default="R", clock justification L, C, or R clock_text_sz: 120 # default=120, clock character size clock_format: "%I:%M" # default="%I:%M", strftime format for clock string + clock_opacity: 1.0 # default=1.0 (0.0-1.0), alpha value of clock overlay menu_text_sz: 40 # default=40, menu character size menu_autohide_tm: 10.0 # default=10.0, time in seconds to show menu before auto hiding (0 disables auto hiding) diff --git a/picframe/model.py b/picframe/model.py index 4987a46..0d78940 100644 --- a/picframe/model.py +++ b/picframe/model.py @@ -26,6 +26,7 @@ 'show_text': "name location", 'text_justify': 'L', 'text_bkg_hgt': 0.25, + 'text_opacity': 1.0, 'fit': False, #'auto_resize': True, 'kenburns': False, @@ -48,6 +49,7 @@ 'clock_justify': "R", 'clock_text_sz': 120, 'clock_format': "%I:%M", + 'clock_opacity': 1.0, #'codepoints': "1234567890AÄÀÆÅÃBCÇDÈÉÊEËFGHIÏÍJKLMNÑOÓÖÔŌØPQRSTUÚÙÜVWXYZaáàãæåäbcçdeéèêëfghiíïjklmnñoóôōøöpqrsßtuúüvwxyz., _-+*()&/`´'•" # limit to 121 ie 11x11 grid_size 'menu_text_sz': 40, 'menu_autohide_tm': 10.0, diff --git a/picframe/viewer_display.py b/picframe/viewer_display.py index 3dd3be7..293a0a9 100644 --- a/picframe/viewer_display.py +++ b/picframe/viewer_display.py @@ -59,6 +59,7 @@ def __init__(self, config): self.__show_text = parse_show_text(config['show_text']) self.__text_justify = config['text_justify'].upper() self.__text_bkg_hgt = config['text_bkg_hgt'] if 0 <= config['text_bkg_hgt'] <= 1 else 0.25 + self.__text_opacity = config['text_opacity'] self.__fit = config['fit'] #self.__auto_resize = config['auto_resize'] self.__kenburns = config['kenburns'] @@ -96,6 +97,7 @@ def __init__(self, config): self.__clock_justify = config['clock_justify'] self.__clock_text_sz = config['clock_text_sz'] self.__clock_format = config['clock_format'] + self.__clock_opacity = config['clock_opacity'] ImageFile.LOAD_TRUNCATED_IMAGES = True # occasional damaged file hangs app @property @@ -372,7 +374,8 @@ def __make_text(self, pic, paused, side=0, pair=False): else: c_rng = self.__display.width * 0.5 - 100 # range for x loc from L to R justified block = pi3d.FixedString(self.__font_file, final_string, shadow_radius=3, font_size=self.__show_text_sz, - shader=self.__flat_shader, justify=self.__text_justify, width=c_rng) + shader=self.__flat_shader, justify=self.__text_justify, width=c_rng, + color=(255, 255, 255, int(255 * float(self.__text_opacity)))) adj_x = (c_rng - block.sprite.width) // 2 # half amount of space outside sprite if self.__text_justify == "L": adj_x *= -1 @@ -398,7 +401,8 @@ def __draw_clock(self): if current_time != self.__prev_clock_time: width = self.__display.width - 50 self.__clock_overlay = pi3d.FixedString(self.__font_file, current_time, font_size=self.__clock_text_sz, - shader=self.__flat_shader, width=width, shadow_radius=3) + shader=self.__flat_shader, width=width, shadow_radius=3, + color=(255, 255, 255, int(255 * float(self.__clock_opacity)))) x = (width - self.__clock_overlay.sprite.width) // 2 if self.__clock_justify == "L": x *= -1 From 527900356871f1fca8c302bfb5a61a2c9b14356b Mon Sep 17 00:00:00 2001 From: Simon Trimmer Date: Sat, 23 Apr 2022 21:56:54 +0100 Subject: [PATCH 8/9] Introduce geo_suppress_list to omit strings from location text Allows you to remove text from the location text, for instance when most of your photos are from one location or the reverse geo results for a location is extremely verbose. --- picframe/config/configuration_example.yaml | 1 + picframe/model.py | 1 + picframe/viewer_display.py | 12 +++++++++++- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/picframe/config/configuration_example.yaml b/picframe/config/configuration_example.yaml index f16f25a..13e75e3 100644 --- a/picframe/config/configuration_example.yaml +++ b/picframe/config/configuration_example.yaml @@ -41,6 +41,7 @@ viewer: menu_text_sz: 40 # default=40, menu character size menu_autohide_tm: 10.0 # default=10.0, time in seconds to show menu before auto hiding (0 disables auto hiding) + geo_suppress_list: [], # default=None, substrings to remove from the location text model: pic_dir: "~/Pictures" # default="~/Pictures", root folder for images diff --git a/picframe/model.py b/picframe/model.py index 4987a46..8b057c5 100644 --- a/picframe/model.py +++ b/picframe/model.py @@ -51,6 +51,7 @@ #'codepoints': "1234567890AÄÀÆÅÃBCÇDÈÉÊEËFGHIÏÍJKLMNÑOÓÖÔŌØPQRSTUÚÙÜVWXYZaáàãæåäbcçdeéèêëfghiíïjklmnñoóôōøöpqrsßtuúüvwxyz., _-+*()&/`´'•" # limit to 121 ie 11x11 grid_size 'menu_text_sz': 40, 'menu_autohide_tm': 10.0, + 'geo_suppress_list': [], }, 'model': { diff --git a/picframe/viewer_display.py b/picframe/viewer_display.py index 3dd3be7..cc78ebc 100644 --- a/picframe/viewer_display.py +++ b/picframe/viewer_display.py @@ -60,6 +60,7 @@ def __init__(self, config): self.__text_justify = config['text_justify'].upper() self.__text_bkg_hgt = config['text_bkg_hgt'] if 0 <= config['text_bkg_hgt'] <= 1 else 0.25 self.__fit = config['fit'] + self.__geo_suppress_list = config['geo_suppress_list'] #self.__auto_resize = config['auto_resize'] self.__kenburns = config['kenburns'] if self.__kenburns: @@ -358,7 +359,16 @@ def __make_text(self, pic, paused, side=0, pair=False): fdt = time.strftime(self.__show_text_fm, time.localtime(pic.exif_datetime)) info_strings.append(fdt) if (self.__show_text & 16) == 16 and pic.location is not None: # location - info_strings.append(pic.location) #TODO need to sanitize and check longer than 0 for real + location = pic.location + # search for and remove substrings from the location text + if self.__geo_suppress_list is not None: + for part in self.__geo_suppress_list: + location = location.replace(part, "") + # remove any redundant concatination strings once the substrings have been removed + location = location.replace(" ,", "") + # remove any trailing commas or spaces from the location + location = location.strip(", ") + info_strings.append(location) #TODO need to sanitize and check longer than 0 for real if (self.__show_text & 32) == 32: # folder info_strings.append(os.path.basename(os.path.dirname(pic.fname))) if paused: From b740d456b0438c7fba6a060e2448a06cfaf6db64 Mon Sep 17 00:00:00 2001 From: helgeerbe Date: Fri, 20 May 2022 14:48:44 +0200 Subject: [PATCH 9/9] remove colon in geo_suppress_list example config --- picframe/config/configuration_example.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/picframe/config/configuration_example.yaml b/picframe/config/configuration_example.yaml index 9121265..6f05253 100644 --- a/picframe/config/configuration_example.yaml +++ b/picframe/config/configuration_example.yaml @@ -43,7 +43,7 @@ viewer: menu_text_sz: 40 # default=40, menu character size menu_autohide_tm: 10.0 # default=10.0, time in seconds to show menu before auto hiding (0 disables auto hiding) - geo_suppress_list: [], # default=None, substrings to remove from the location text + geo_suppress_list: [] # default=None, substrings to remove from the location text model: pic_dir: "~/Pictures" # default="~/Pictures", root folder for images