helgeerbe / picframe

Picture frame viewer for raspi, controlled via mqtt and automatticly integrated as mqtt device in homeassistant.
MIT License
113 stars 32 forks source link

Feature Request: Power Down via MQTT #397

Open NateEaton opened 3 months ago

NateEaton commented 3 months ago

Would it be possible to add support for a message received by MQTT client that triggers executing a shutdown command for the Pi?

TL;DR With my current photoframe on a Pi3B, it is attached to a monitor via DVI. I set up PicFrame with the Bookworm Lite OS to test as a replacement. When I try the display off option (via HTTP from browser or MQTT from my Home Assistant instance), I get log messages about 0 not being a valid option for xset. Based on reading elsewhere online, it sounds like the display control is only likely to work with an HDMI display. So, I'd like to be able to do what I have done with my old photoframe: using an HA automation, at bedtime send a power down command to the Pi via MQTT (that photoframe Pi has mqtt_shutdown installed) and 15 seconds later turn off the smartplug to the Pi and monitor. Another automation turns on the smartplug in the morning; the monitor comes on and the Pi boots up to the photoframe software. As Picframe already has the ability to power down (via peripherals with P command), it seems like a relatively straightforward enhancement to add an MQTT power switch or button that calls similar code to the peripheral power down option.

paddywwoof commented 3 months ago

Hi, rather than turning the screen off via picframe have you tried the 'stop' option. You're going to power down anyway so it's just a matter of tidying any disk reads and writes. This is just from memory, I will have look at it on RPi later.

helgeerbe commented 3 months ago

Hi,

my actual solution in a Home Assistant automation:

  1. blank scrreen (mqtt)
  2. remote shell command: ssh pi@picframe 'sudo halt'
  3. wait 10 sec
  4. power off smart switch
helgeerbe commented 3 months ago

Here is my actual automation:

Picframe is turned off:

  1. switch turned off
  2. no one at home
  3. google calendar
alias: FrameTurnOff
description: Bilderrahmen über den homeassistant google calendar ausschalten
trigger:
  - platform: calendar
    event: end
    offset: "0:0:0"
    entity_id: calendar.homeassistant
    id: Calendar
  - platform: state
    entity_id: group.residents
    to: not_home
    id: Bewohner
  - platform: state
    entity_id: input_boolean.picframe_power
    from: "on"
    to: "off"
    id: Power_off
condition:
  - condition: or
    conditions:
      - condition: and
        conditions:
          - condition: trigger
            id:
              - Power_off
              - Bewohner
          - condition: device
            type: is_on
            device_id: 7097dfc77369d5a50f7a1968800171bc
            entity_id: switch.picframe_power
            domain: switch
      - condition: and
        conditions:
          - condition: trigger
            id: Calendar
          - condition: template
            value_template: "{{ '#Frame' in trigger.calendar_event.summary }}"
          - condition: device
            type: is_on
            device_id: 7097dfc77369d5a50f7a1968800171bc
            entity_id: switch.picframe_power
            domain: switch
action:
  - type: turn_off
    device_id: 44e83c25884c5f5398217f9fb47e3178
    entity_id: switch.picframe_display
    domain: switch
  - service: shell_command.shutdown_picframe
    data: {}
  - delay:
      hours: 0
      minutes: 0
      seconds: 10
      milliseconds: 0
  - type: turn_off
    device_id: 7097dfc77369d5a50f7a1968800171bc
    entity_id: switch.picframe_power
    domain: switch
  - service: input_boolean.turn_off
    target:
      entity_id: input_boolean.picframe_power
    data: {}
mode: queued
paddywwoof commented 3 months ago

OK. There is a Controller.stop() method https://github.com/helgeerbe/picframe/blob/1bbc5279219fedbbc8f8a3b243ab7a344240a441/src/picframe/controller.py#L336 that can be called by InterfaceHttp using http and js. Also, I think using https://github.com/helgeerbe/picframe/blob/1bbc5279219fedbbc8f8a3b243ab7a344240a441/src/picframe/interface_mqtt.py#L192

Turning the screen off doesn't really matter as you are going to sudo halt and power off. What you need to do is flush any buffers and finish writing to the db. Though that's only going to happen if you've just uploaded lots of pictures at the eleventh hour (literally).

However, now that I actually try this on the running picframe here, I find that running the http server hangs as the whole process gets stuck in an infinite loop waiting for a clear flag to be set. Picframe has several threads looping (python style pseudo threads) and there is signalling between them, but it's a bit hacky and obviously has some bug or other. The fact that the http server stops but the viewer_display continues to loop (but doesn't change the image) makes me think that it's getting stuck in the ImageCache.stop() routine, presumably waiting for the the __loop() to break! I might try and debug this later, but in the mean time you could try using the mqtt stop and see if it works.

paddywwoof commented 3 months ago

OK after a quick check I can see that it's the HTTPServer.shutdown() method that's not returning. It probably needs to be called from a second thread. Indeed, if I change the method as below it works:

    def stop(self):
        t = threading.Thread(target=self.shutdown)
        t.daemon = True
        t.start()

Another fix to add to the list!

paddywwoof commented 3 months ago

Actually having gone back to restart the picture frame in my kitchen I've just checked the interface_mqtt code and I can see that there's a TODO. At the moment we just hack into the Controller.keep_looping property setting it to False. This needs to be changed to

        # stop loops and end program
        elif message.topic == self.__device_id + "/stop":
            self.__controller.stop()

@helgeerbe I will add this to the other fixes and push them up to a github branch. Does the InterfaceMQTT.stop() work fine? I haven't used mqtt for ages.

Paddy

helgeerbe commented 3 months ago

@paddywwoof never used it before. But I just tested it and it works.

I run picframe as a service, which will be restarted if the process stops. So stopping picframe is more a restart here.

paddywwoof commented 3 months ago

@helgeerbe Ah yes. I always comment out the 'always restart' line in picframe.service so I can fiddle about stopping and starting as I adjust things. @NateEaton I suppose you would need to do the same if you want to stop the picture frame starting up again.

I just checked out the dev branch on a new setup RPi5 to see what the 400% CPU usage was all about on @saphno general setup instructions. I found that the X11 system draws so much current that my budget USB power source can't keep up and the RPi just dies after showing the first image for a few seconds. So that seems a non-starter.

I revisited your wayfire instructions https://github.com/helgeerbe/picframe/wiki/Setup-guide-Bookworm-and-Wayland and can reconfirm that this uses less than 1% CPU and all seems to work fine. Apart from the screen blanking. Also I found that libsdl2-dev isn't automatically installed as a dependency of pip install pysdl2 which sent me round in circles for ages. I will try to do some more experiments with picframe on RPi5 tomorrow but I suspect we will be stuck with a different system from the earlier RPis.

Paddy

sapnho commented 3 months ago

I just checked out the dev branch on a new setup RPi5 to see what the 400% CPU usage was all about on @saphno general setup instructions. I found that the X11 system draws so much current that my budget USB power source can't keep up and the RPi just dies after showing the first image for a few seconds. So that seems a non-starter.

Wow, @paddywwoof, at least the power supply worked in my case! :-) The other installation instruction is not really an option because of the screen blanking issue.

Maybe it's just a small thing? Hope never dies...

paddywwoof commented 3 months ago

Yes I want to fix the screen blanking mainly. Someone might have found something over the past few months. However, I've just been pausing and turning the brightness down to zero on my frame running here, and that seems preferable to high CPU load. Anyway fingers crossed to find a better solution.