小爱同学和树莓派开关电脑,加强版。-Arduino中文社区 - Powered by Discuz! Archiver

南岛孤云 发表于 2021-5-6 18:47

小爱同学和树莓派开关电脑,加强版。

本帖最后由 南岛孤云 于 2021-5-9 13:00 编辑

以前有人做了个树莓派开机的,貌似只能开机。我这个改进了下,可以开,可以关。可以小爱控制。
Win10需要开openssh server,win10自带,开启就可以了。
已加入小爱控制,小爱把它当个插座,app里设备名字改成电脑就好。
需要电脑有局域网固定IP,用来ping电脑状态,可以在网卡或者路由上设置。
App里图标用滑动开关,类型用'开关按键', 就可以正确的表现电脑的开关状态。另外需要wakeonlan 以及 paramiko
sudo pip3 install wakeonlan
sudo pip3 install paramiko

另外注意下目前Github上blinker的python包有bug,
记得用pull request里那个100%CPU占用的修正。
我的树莓派3B+,这个后台开着应该是基本不占资源。
===================
更新了两句开关机超时的判定,默认都是60秒
设定超时的原因在于开关命令发出后,一般电脑要几十秒后才彻底完成开关动作,程序会通过ping的方式来确定电脑状态
如果出现意外,开关机一直无法完成(开机1分钟ping不到,或者关机1分钟一直能ping到),就要通过超时来停止ping,然后取消开关运行保护。
这样的结构一举三得,
1,避免重复开关机,最起码电脑要实际完成开关动作或者超时以后才能再发出指令,当然小爱是不知道的,你一直让她开她也会去开,但只是去帮你按了开关而已,逻辑都在开关上。
2,正确显示电脑状态,比如你手动开关的电脑,点灯不知道,最坏的情况下,一个心跳以后状态也同步了。
3,因为是纯软件的方法,一个设备就是个进程而已,不需要每个电脑去接线,所以你如果有多台电脑,完全可以一个派控制所有的。
===================
更新下win10下开ssh服务的步骤,以及需要注意的几个点。
开始 - 设置 - 应用 - 可选功能 - 添加功能 - OPENssh服务器 选择安装
然后打开PowerShell
Start-Service sshd
# 这一步开启ssh服务的自动启动,或者你也可以到 搜索-服务 里找到openssh ssh server这一条,用鼠标开启自动:
Set-Service -Name sshd -StartupType 'Automatic'
# 检查一下防火墙设置.
Get-NetFirewallRule -Name *ssh*
# 应该可以看到一条 "OpenSSH-Server-In-TCP",而且已经开启了
# 如果找不到,执行一下这条添加新的防火墙规则,如果自行更改了端口 注意同时修改我们python代码上面paramiko的参数,具体加在client.connect(port=xxxx)
New-NetFirewallRule -Name sshd -DisplayName 'OpenSSH Server (sshd)' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22
默认的win10 openssh配置文件在 C:\ProgramData\ssh\sshd_config
如果你打算用公匙私匙方式来登录shh,这个文件的最后两句要用#注释掉,这两句会造成你安装在你管理员帐号下.ssh的密匙不起作用(密匙安装位置在C:\Users\xxxx\.ssh)。
#Match Group administrators
       #AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys
配置完成后,最好在派上终端里手动登录一次,第一次登录好像需要输入确定。
==================================


```
#!/usr/bin/env python
# -*- coding: utf-8 -*-

# 树莓派利用WOL和OPENSSH实现开关机。 针对WIN10, 电脑需要openssh!
# 直接拿去用需要改5处,1,密匙,2,局域网电脑的固定IP,3,电脑ssh用户名,4,电脑ssh密码 5,电脑的MAC地址

from Blinker import Blinker, BlinkerButton, BlinkerNumber, BlinkerMIOT
from Blinker.BlinkerConfig import *
from Blinker.BlinkerDebug import *
from wakeonlan import send_magic_packet
import paramiko
import time
import subprocess



auth = 'xxxxxxxxxx'# 1,点灯app上获得的密匙

BLINKER_DEBUG.debugAll()

Blinker.mode("BLINKER_WIFI")
Blinker.miotType('BLINKER_MIOT_OUTLET')
Blinker.begin(auth)

staticip = "192.168.1.200"# 2,电脑局域网固定IP,用于检测电脑开关状态以及利用SSH关机,改为你的设置
pcusr = 'xxxxxx'# 3,电脑ssh用户名
pcpw = 'xxxxxx'# 4,电脑ssh密码

pcmac = 'ff.ff.ff.ff.ff.ff'# 5,MAC地址,改成你自己电脑网卡的

button1 = BlinkerButton("btn-pc1")# 数据键,在App里设置一个一样的开关,类型为 '开关按键',图标用滑动开关,其他随意,文本可为空
cmd1 = "timeout 0.1 ping -c 1 " + staticip# 电脑开关检测就是一个局域网内的ping,超时我设置为100ms,貌似太短或太长小爱都容易出错
lockbutton1 = False

oState = ''

def miotPowerState(state):
    ''' '''

    global oState

    BLINKER_LOG('need set power state: ', state)

    oState = state
    BlinkerMIOT.powerState(state)
    BlinkerMIOT.print()
# 小爱控制的实际部分放在上报状态之后,因为电脑开机实际时间很长,小爱等久了她会以为没开
    if state == 'true':
      button1_callback('on')
    elif state == 'false':
      button1_callback('off')

def miotQuery(queryCode):
    ''' '''

    global oState

# 问小爱电脑开了吗,ping一次获得电脑实际状态
    if subprocess.call(cmd1, shell=True)==0:
      oState = 'true'
    else:
      oState = 'false'


    BLINKER_LOG('MIOT Query codes: ', queryCode)

    if queryCode == BLINKER_CMD_QUERY_ALL_NUMBER :
      BLINKER_LOG('MIOT Query All')
      BlinkerMIOT.powerState(oState)
      BlinkerMIOT.print()
    elif queryCode == BLINKER_CMD_QUERY_POWERSTATE_NUMBER :
      BLINKER_LOG('MIOT Query Power State')
      BlinkerMIOT.powerState(oState)
      BlinkerMIOT.print()
    else :
      BlinkerMIOT.powerState(oState)
      BlinkerMIOT.print()


# 关机部分用paramiko的sshclient,不用密码的话可以改用密匙,具体查阅paramiko用法
def shutdownpc():

    global staticip
    global pcusr
    global pcpw

    client = paramiko.SSHClient()
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    client.connect(staticip, username=pcusr, password=pcpw)
    stdin, stdout, stderr = client.exec_command('shutdown -s -f -c "小爱将在10秒内关闭这个电脑" -t 10')

    if client is not None:
      client.close()
      del client, stdin, stdout, stderr


# 开关键,集成了开关功能,状态报告给小爱,开关过程中的运行保护,开关后状态的更新。
def button1_callback(state):
    """ """
    global lockbutton1
    global oState
    global pcmac
    dtimeout = 60# 开关机超时默认60秒

    if lockbutton1==False:
      BLINKER_LOG('get button state: ', state)
      if state=='on':
            if subprocess.call(cmd1, shell=True)==0:
                oState = 'true'
                Blinker.print("检测到电脑已开,按钮状态已更新")
                button1.text('已开机')
                button1.print(state)
            else:
                Blinker.print("发送开机指令...")
                oState = 'true'
                lockbutton1 = True
                tic = time.perf_counter()
                toc = time.perf_counter()
                send_magic_packet(pcmac)# 发魔术包开机
                while subprocess.call(cmd1, shell=True)!=0 and toc-tic<dtimeout+2:
                  time.sleep(2)
                  toc = time.perf_counter()
                if toc-tic >= dtimeout:
                  Blinker.print("开机超时!")
                  button1.text('已关机')
                  button1.print('off')
                else:
                  button1.text('已开机')
                  button1.print(state)
                lockbutton1 = False
      elif state=='off':
            if subprocess.call(cmd1, shell=True)==0:
                Blinker.print("发送关机指令...")
                oState = 'false'
                lockbutton1 = True
                tic = time.perf_counter()
                toc = time.perf_counter()
                shutdownpc()# 关机
                while subprocess.call(cmd1, shell=True)==0 and toc-tic<dtimeout+2:
                  time.sleep(2)
                  toc = time.perf_counter()
                if toc-tic >= dtimeout:
                  Blinker.print("关机超时!")
                  button1.text('已开机')
                  button1.print('on')
                else:
                  button1.text('已关机')
                  button1.print(state)
                lockbutton1 = False
            else:
                oState = 'false'
                Blinker.print("检测到电脑已关闭,按钮状态已更新")
                button1.text('已关机')
                button1.print(state)
    else:
      Blinker.print("正在开机或关机中..")

# 心跳加入了电脑状态检测,更新按钮
def heartbeat_callback():

    global oState

    if subprocess.call(cmd1, shell=True)==0:
      oState = 'true'
      button1.text('已开机')
      button1.print("on")
    else:
      oState = 'false'
      button1.text('已关机')
      button1.print("off")


button1.attach(button1_callback)
Blinker.attachHeartbeat(heartbeat_callback)

BlinkerMIOT.attachPowerState(miotPowerState)
BlinkerMIOT.attachQuery(miotQuery)

if __name__ == '__main__':

    while True:
      Blinker.run()
```










奈何col 发表于 2021-5-6 19:07

可以可以,大家都想的是接物理开关去关,缺忘了可以ssh连上去关

小明没吃饱 发表于 2021-10-1 21:38

大佬 请问下我这个是什么原因无法运行
Traceback (most recent call last):
File "p.py", line 21, in <module>
    Blinker.begin(auth)
File "/usr/local/lib/python3.7/dist-packages/Blinker-0.2.0-py3.7.egg/Blinker/Blinker.py", line 171, in begin
    bProto.conn1.start(auth, bProto.aliType, bProto.duerType, bProto.miType)
File "/usr/local/lib/python3.7/dist-packages/Blinker-0.2.0-py3.7.egg/BlinkerAdapters/BlinkerMQTT.py", line 328, in start
    self.client.connect(self.bmqtt.host, self.bmqtt.port, 60)
File "/usr/local/lib/python3.7/dist-packages/paho_mqtt-1.5.1-py3.7.egg/paho/mqtt/client.py", line 940, in connect
    bind_address, bind_port, clean_start, properties)
File "/usr/local/lib/python3.7/dist-packages/paho_mqtt-1.5.1-py3.7.egg/paho/mqtt/client.py", line 1005, in connect_async
    raise ValueError('Invalid host.')
ValueError: Invalid host.

小明没吃饱 发表于 2021-10-2 01:23

小明没吃饱 发表于 2021-10-1 21:38
大佬 请问下我这个是什么原因无法运行
Traceback (most recent call last):
File "p.py", line 21, in


知道了 是python库bug

custsly 发表于 2021-10-24 13:16

感谢大佬提供的新思路,我之前是在win10上跑一个web服务暴露接口用于关机和重启,直接通过ssh就可以把这个去掉了

KeilBill 发表于 2022-1-16 09:42

小明没吃饱 发表于 2021-10-2 01:23
知道了 是python库bug

你好我也遇到这个问题了,你是怎么解决的呢
页: [1]
查看完整版本: 小爱同学和树莓派开关电脑,加强版。