修复切换WIFI后 ssdp无法扫描到问题

修复未获取到下位机版本号时会升级的问题
新增P800配置

Squashed commit of the following:
commit f20f64f6e27842a6a76302a8a9dff32d3db66cc9
Merge: f546156 b48fdc7
Author: zkk <1007518571@qq.com>
Date:   Sat Jun 21 11:17:37 2025 +0800

    Merge commit 'b48fdc708c052b6c4342db499307518db13d702b' into release
This commit is contained in:
张开科 2025-06-21 11:38:47 +08:00
parent b05f6b5b02
commit 9c91d48cfb
3 changed files with 173 additions and 23 deletions

131
config/moonraker_P800.conf Normal file
View File

@ -0,0 +1,131 @@
[server]
host: 0.0.0.0
port: %PORT%
klippy_uds_address: %UDS%
[authorization]
trusted_clients:
%LAN%
10.0.0.0/8
127.0.0.0/8
169.254.0.0/16
172.16.0.0/12
192.168.0.0/16
FE80::/10
::1/128
cors_domains:
*.lan
*.local
*://localhost
*://localhost:*
*://my.mainsail.xyz
*://app.fluidd.xyz
[octoprint_compat]
[firmware_manager]
[history]
[power Printer]
type: gpio
pin: gpiochip4/gpio13
initial_state: on
off_when_shutdown: true
off_when_shutdown_delay: 86400
on_when_job_queued: true
locked_while_printing: true
restart_klipper_when_powered: true
# restart_delay: 1
bound_services:
klipper
KlipperScreen
crowsnest
[button switch_button]
type: gpio
pin: ^gpiochip4/gpio12
debounce_period: .02
minimum_event_time: 0
on_press:
{% if event.elapsed_time > 5.0 %}
{% do call_method("machine.shutdown") %}
{% endif %}
on_release:
{% do call_method("machine.device_power.post_device", device="Printer", action="toggle") %}
[button power_outage]
type: gpio
pin: ^!gpiochip4/gpio19
debounce_period: .01
minimum_event_time: 0
on_press:
{% do call_method("printer.gcode.script", script="TURN_OFF_HEATERS") %}
{% do call_method("printer.gcode.script", script="GET_TASKLINE") %}
{% set query_objs = {"print_stats": ["state"], "toolhead": ["extruder"], "virtual_sdcard": ["file_path", "file_position", "file_line"]} %}
{% set status = call_method("printer.objects.query", objects=query_objs) %}
{% do call_method("printer.emergency_stop") %}
{% set data = status | tojson | fromjson %}
{% set print_state = data['status']['print_stats']['state'] %}
# Judging the printer status
{% if print_state | string == 'printing' or print_state | string == 'paused' %}
{% set hotend = data['status']['toolhead']['extruder'] %}
{% set position = data['status']['virtual_sdcard']['file_position'] %}
{% set line = data['status']['virtual_sdcard']['file_line'] %}
{% set filepath = data['status']['virtual_sdcard']['file_path'] %}
# save file position and line
{% do call_method("printer.gcode.script", script="SAVE_VARIABLE VARIABLE=power_resume_position VALUE=" + position | string) %}
{% do call_method("printer.gcode.script", script="SAVE_VARIABLE VARIABLE=power_resume_line VALUE=" + line | string) %}
{% set script = "SAVE_VARIABLE VARIABLE=power_loss_paused VALUE=" ~ ("False" if print_state | string == 'printing' else "True") %}
{% if print_state | string in ['printing', 'paused'] %}
{% do call_method("printer.gcode.script", script=script) %}
{% endif %}
# save extruder
{% do call_method("printer.gcode.script", script="SAVE_VARIABLE VARIABLE=power_resume_extruder VALUE=\"'" + hotend | string + "'\"") %}
# save file
{% do call_method("printer.gcode.script", script="SAVE_VARIABLE VARIABLE=filepath VALUE=\"'" + filepath | string + "'\"") %}
save interrupt
{% do call_method("printer.gcode.script", script="SAVE_VARIABLE VARIABLE=was_interrupted VALUE=True") %}
{% else %}
{% do call_method("printer.gcode.script", script="SAVE_VARIABLE VARIABLE=was_interrupted VALUE=False") %}
{% endif %}
# shutdown
{% do call_method("machine.shutdown") %}
[update_manager]
channel: stable
refresh_interval: 168
[update_manager klipper]
channel: stable
[update_manager KlipperScreen]
type: git_repo
path: /home/klipper/KlipperScreen
origin: https://github.com/CreatBotOfficail/CreatBotKlipperScreen.git
env: /home/klipper/.KlipperScreen-env/bin/python
requirements: scripts/KlipperScreen-requirements.txt
install_script: scripts/KlipperScreen-install.sh
channel: stable
primary_branch: P800
[update_manager mainsail-config]
type: git_repo
primary_branch: master
path: ~/mainsail-config
origin: https://github.com/mainsail-crew/mainsail-config.git
managed_services: klipper
[update_manager mainsail]
type: web
channel: stable
repo: mainsail-crew/mainsail
path: ~/mainsail
# Crowsnest update_manager entry
[update_manager crowsnest]
type: git_repo
path: ~/crowsnest
origin: https://github.com/CreatBotOfficail/CreatBotCrowsnest.git
managed_services: crowsnest
install_script: tools/pkglist.sh

View File

@ -118,6 +118,8 @@ class FirmwareUpdate:
logging.error(f"Error querying {mcu}: {e}") logging.error(f"Error querying {mcu}: {e}")
def _compare_versions(self, version1, version2): def _compare_versions(self, version1, version2):
if not version1 or not version2:
return 0
v1_parts = [int(part) for part in version1.split('.') if part] v1_parts = [int(part) for part in version1.split('.') if part]
v2_parts = [int(part) for part in version2.split('.') if part] v2_parts = [int(part) for part in version2.split('.') if part]
@ -139,6 +141,9 @@ class FirmwareUpdate:
logging.info(f"klipper version: {self.klipper_version}") logging.info(f"klipper version: {self.klipper_version}")
for mcu in self.mcu_info: for mcu in self.mcu_info:
mcu_version = self.mcu_info[mcu].get('mcu_version', "") mcu_version = self.mcu_info[mcu].get('mcu_version', "")
if not mcu_version:
self.mcu_info[mcu]['need_update'] = False
continue
mcu_vs_min = self._compare_versions(mcu_version, self.min_version) mcu_vs_min = self._compare_versions(mcu_version, self.min_version)
mcu_vs_klipper = self._compare_versions(mcu_version, self.klipper_version) mcu_vs_klipper = self._compare_versions(mcu_version, self.klipper_version)

View File

@ -138,8 +138,7 @@ class ZeroconfRegistrar:
name = f"{instance_name} ({host})" name = f"{instance_name} ({host})"
if len(name) > 64: if len(name) > 64:
name = instance_name name = instance_name
await self.ssdp_server.start() await self.ssdp_server.start(addr, name, hi["port"])
self.ssdp_server.register_service(name, addr, hi["port"])
async def close(self) -> None: async def close(self) -> None:
await self.runner.unregister_services([self.service_info]) await self.runner.unregister_services([self.service_info])
@ -151,6 +150,14 @@ class ZeroconfRegistrar:
addresses = [x for x in self._extract_ip_addresses(network)] addresses = [x for x in self._extract_ip_addresses(network)]
self.service_info.addresses = addresses self.service_info.addresses = addresses
await self.runner.update_services([self.service_info]) await self.runner.update_services([self.service_info])
if self.ssdp_server is not None:
machine: Machine = self.server.lookup_component("machine")
addr = machine.public_ip
if not addr:
addr = f"{self.mdns_name}.local"
name = self.ssdp_server.name
hi = self.server.get_host_info()
await self.ssdp_server.update_service(addr, name, hi["port"])
def _extract_ip_addresses(self, network: Dict[str, Any]) -> Iterator[bytes]: def _extract_ip_addresses(self, network: Dict[str, Any]) -> Iterator[bytes]:
for ifname, ifinfo in network.items(): for ifname, ifinfo in network.items():
@ -208,6 +215,7 @@ class SSDPServer(asyncio.protocols.DatagramProtocol):
self.running: bool = False self.running: bool = False
self.close_fut: Optional[asyncio.Future] = None self.close_fut: Optional[asyncio.Future] = None
self.response_handle: Optional[asyncio.TimerHandle] = None self.response_handle: Optional[asyncio.TimerHandle] = None
self.transport: Optional[asyncio.DatagramTransport] = None
eventloop = self.server.get_event_loop() eventloop = self.server.get_event_loop()
self.boot_id = int(eventloop.get_loop_time()) self.boot_id = int(eventloop.get_loop_time())
self.config_id = 1 self.config_id = 1
@ -242,20 +250,22 @@ class SSDPServer(asyncio.protocols.DatagramProtocol):
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, ip_combo) sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, ip_combo)
return sock return sock
async def start(self) -> None: async def start(self, addr: str, name: str, port: int) -> None:
if self.running: if self.running:
return await self.stop()
try: try:
sock = self._create_ssdp_socket() sock = self._create_ssdp_socket(source_addr=(addr, 0))
sock.settimeout(0) sock.settimeout(0)
sock.setblocking(False) sock.setblocking(False)
sock.bind(("", SSDP_ADDR[1])) sock.bind(("", SSDP_ADDR[1]))
_loop = asyncio.get_running_loop() _loop = asyncio.get_running_loop()
ret = await _loop.create_datagram_endpoint(lambda: self, sock=sock) ret = await _loop.create_datagram_endpoint(lambda: self, sock=sock)
self.transport, _ = ret self.transport, _ = ret
except (socket.error, OSError): self.running = True
self.register_service(name, addr, port)
except (socket.error, OSError) as e:
logging.error(f"Failed to start SSDP server: {e}")
return return
self.running = True
async def stop(self) -> None: async def stop(self) -> None:
if not self.running: if not self.running:
@ -265,18 +275,22 @@ class SSDPServer(asyncio.protocols.DatagramProtocol):
if self.response_handle is not None: if self.response_handle is not None:
self.response_handle.cancel() self.response_handle.cancel()
self.response_handle = None self.response_handle = None
if self.transport.is_closing(): if self.transport is not None and not self.transport.is_closing():
logging.info("Transport already closing") for notification in self._build_notifications("ssdp:byebye"):
return self.transport.sendto(notification, SSDP_ADDR)
for notification in self._build_notifications("ssdp:byebye"): self.close_fut = self.server.get_event_loop().create_future()
self.transport.sendto(notification, SSDP_ADDR) self.transport.close()
self.close_fut = self.server.get_event_loop().create_future() try:
self.transport.close() await asyncio.wait_for(self.close_fut, 2.)
try: except asyncio.TimeoutError:
await asyncio.wait_for(self.close_fut, 2.) logging.warning("Timeout waiting for SSDP transport to close")
except asyncio.TimeoutError: self.close_fut = None
pass self.transport = None
self.close_fut = None
async def update_service(self, addr: str, name: str, port: int) -> None:
if self.running:
await self.stop()
await self.start(addr, name, port)
def register_service( def register_service(
self, name: str, host_name_or_ip: str, port: int self, name: str, host_name_or_ip: str, port: int
@ -292,7 +306,6 @@ class SSDPServer(asyncio.protocols.DatagramProtocol):
model = self.name.rsplit("-", 1)[0] model = self.name.rsplit("-", 1)[0]
if "(" in self.name and ")" in self.name: if "(" in self.name and ")" in self.name:
device_name = self.name.split("(", 1)[1].split(")", 1)[0] device_name = self.name.split("(", 1)[1].split(")", 1)[0]
app: MoonrakerApp = self.server.lookup_component("application")
self.base_url = f"http://{host_name_or_ip}" self.base_url = f"http://{host_name_or_ip}"
self.response_headers = [ self.response_headers = [
f"USN: uuid:{self.serial_number}::upnp:rootdevice::urn:creatbot-com:device:3dprinter:2", f"USN: uuid:{self.serial_number}::upnp:rootdevice::urn:creatbot-com:device:3dprinter:2",
@ -308,7 +321,7 @@ class SSDPServer(asyncio.protocols.DatagramProtocol):
] ]
self.registered = True self.registered = True
advertisements = self._build_notifications("ssdp:alive") advertisements = self._build_notifications("ssdp:alive")
if self.running: if self.running and self.transport is not None:
for ad in advertisements: for ad in advertisements:
self.transport.sendto(ad, SSDP_ADDR) self.transport.sendto(ad, SSDP_ADDR)
self.advertisements = cycle(advertisements) self.advertisements = cycle(advertisements)
@ -335,7 +348,7 @@ class SSDPServer(asyncio.protocols.DatagramProtocol):
) )
def _advertise_presence(self, eventtime: float) -> float: def _advertise_presence(self, eventtime: float) -> float:
if self.running and self.registered: if self.running and self.registered and self.transport is not None:
cur_ad = next(self.advertisements) cur_ad = next(self.advertisements)
self.transport.sendto(cur_ad, SSDP_ADDR) self.transport.sendto(cur_ad, SSDP_ADDR)
delay = random.uniform(SSDP_MAX_AGE / 6., SSDP_MAX_AGE / 3.) delay = random.uniform(SSDP_MAX_AGE / 6., SSDP_MAX_AGE / 3.)
@ -344,6 +357,7 @@ class SSDPServer(asyncio.protocols.DatagramProtocol):
def connection_made( def connection_made(
self, transport: asyncio.transports.BaseTransport self, transport: asyncio.transports.BaseTransport
) -> None: ) -> None:
self.transport = transport
logging.debug("SSDP Server Connected") logging.debug("SSDP Server Connected")
def connection_lost(self, exc: Exception | None) -> None: def connection_lost(self, exc: Exception | None) -> None:
@ -396,7 +410,7 @@ class SSDPServer(asyncio.protocols.DatagramProtocol):
self._respond_to_discovery(addr) self._respond_to_discovery(addr)
def _respond_to_discovery(self, addr: tuple[str | Any, int]) -> None: def _respond_to_discovery(self, addr: tuple[str | Any, int]) -> None:
if not self.running: if not self.running or self.transport is None:
return return
self.response_handle = None self.response_handle = None
response: List[str] = ["HTTP/1.1 200 OK"] response: List[str] = ["HTTP/1.1 200 OK"]