Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rc stable7 7.1.021 #1827

Draft
wants to merge 30 commits into
base: stable7
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
c462731
add a parameter to skip requirements checks
pipiche38 Jan 26, 2025
c2298f5
use CheckRequirements to check or not requirements
pipiche38 Jan 26, 2025
4f2f45c
Restart plugin when NcpFailure
pipiche38 Jan 26, 2025
c515b24
be more selective on the handling of connection_lost exceptions
pipiche38 Jan 26, 2025
08d64d9
update
pipiche38 Jan 26, 2025
b2aac43
disconnect in case of error during shutdown
pipiche38 Jan 26, 2025
6b2c57e
allow socket connection
pipiche38 Jan 27, 2025
6ee9880
better handling of docker
pipiche38 Jan 27, 2025
aa1174d
improving the Docker startup script
pipiche38 Jan 27, 2025
c74b80c
improvement for docker
pipiche38 Jan 27, 2025
a036f73
give a grace period at startup if MAXIMUM_COUNT error
pipiche38 Jan 29, 2025
c6684e5
improve logging messages
pipiche38 Jan 29, 2025
802fe6b
update release notes for 7.1.21
pipiche38 Jan 29, 2025
5745d02
Refactor the fc01 commands to allow new config files
pipiche38 Feb 3, 2025
e6da725
Do not forward ZCL packets to zigpy
pipiche38 Feb 3, 2025
78ab944
Do not forward ZCL packets to zigpy, if they belongs to OTA or Basic …
pipiche38 Feb 3, 2025
eabb481
Fix bug where type_heater was tested against "fip" and "FIP" was given
pipiche38 Feb 4, 2025
d5075f3
fix
pipiche38 Feb 4, 2025
b3e0972
refactor
pipiche38 Feb 4, 2025
9a98432
refactoring
pipiche38 Feb 4, 2025
03cc954
create XIAOMI folder for OTA
pipiche38 Feb 5, 2025
1f8caf2
Allow Xiaomi OTA
pipiche38 Feb 5, 2025
d6de012
refactor default_response_disable. Do not forward ZCL messages to zigpy
pipiche38 Feb 5, 2025
153f8ea
Update get_device_nickname() to return nwkid if the ZDeviceName is empty
pipiche38 Feb 5, 2025
cfc37db
costmetic
pipiche38 Feb 5, 2025
1688ffc
move a log to debug level
pipiche38 Feb 5, 2025
ac9955b
fix issue
pipiche38 Feb 5, 2025
72a7c8f
do not do auto domoMaj, as it will be performed by update_battery
pipiche38 Feb 8, 2025
7f82213
Send default response is needed for write_attribute_request
pipiche38 Feb 8, 2025
75ce031
refactor and cleanup code
pipiche38 Feb 9, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions Classes/LoggingManagement.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ def open_logging_mode(self):
import sys

if not self.pluginconf.pluginConf["enablePluginLogging"]:
domoticz_status_api("Z4D Plugin logs are redirected to Domoticz log")
return

_pluginlogs = Path(self.pluginconf.pluginConf["pluginLogs"] )
Expand All @@ -147,7 +148,7 @@ def open_logging_mode(self):
_backupCount = int(self.pluginconf.pluginConf["loggingBackupCount"])
if "loggingMaxMegaBytes" in self.pluginconf.pluginConf:
_maxBytes = int(self.pluginconf.pluginConf["loggingMaxMegaBytes"]) * 1024 * 1024
domoticz_status_api("Please watch plugin log into %s" % _logfilename)
domoticz_status_api("Z4D Please watch plugin logs into %s" % _logfilename)
if _maxBytes == 0:
# Enable TimedRotating
if sys.version_info >= (3, 9):
Expand Down Expand Up @@ -464,7 +465,7 @@ def loggingWriteErrorHistory(self):


def start_logging_thread(self):
domoticz_log_api("start_logging_thread")
#domoticz_log_api("start_logging_thread")
if self.logging_thread:
domoticz_error_api("start_logging_thread - Looks like logging_thread already started !!!")
return
Expand All @@ -478,7 +479,7 @@ def start_logging_thread(self):

def logging_thread(self):

domoticz_log_api("logging_thread - listening")
#domoticz_log_api("logging_thread - listening")
while self.running:
# We loop until self.running is set to False,
# which indicate plugin shutdown
Expand Down
1 change: 1 addition & 0 deletions Classes/OTA.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
"Salus": {"Folder": "SALUS", "ManufCode": 0x1078, "ManufName": "Computime", "Enabled": True},
"Schneider": {"Folder": "SCHNEIDER-WISER", "ManufCode": 0x105E, "ManufName": "Schneider Electric", "Enabled": True},
"SonOff": {"Folder": "SONOFF", "ManufCode": 0x1286, "ManufName": "Sonoff", "Enabled": True},
"Xiaomi": {"Folder": "XIAOMI", "ManufCode": 0x115f, "ManufName": "Xiaomi", "Enabled": True},
}


Expand Down
1 change: 1 addition & 0 deletions Classes/PluginConf.py
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,7 @@
"Order": 99,
"param": {
# Just for compatibility keep it but hidden ( move to Custom device "Param" section)
"CheckRequirements": {"type": "bool","default": 1,"current": None,"restart": 0,"hidden": True,"Advanced": True},
"nPDUaPDUThreshold": {"type": "bool","default": 0,"current": None,"restart": 0,"hidden": True,"Advanced": True,"ZigpyRadio": ""},
"rebindLivolo": {"type": "bool","default": 0,"current": None,"restart": 0,"hidden": True,"Advanced": False,},
"allowAutoPairing": {"type": "bool","default": 0,"current": None,"restart": 0,"hidden": True,"Advanced": True,},
Expand Down
6 changes: 3 additions & 3 deletions Classes/WebServer/com.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

def startWebServer(self):

self.logging("Status", "WebUI thread started")
self.logging("Debug", "WebUI thread started")
if self.server_thread:
self.logging("Error", "start_logging_thread - Looks like logging_thread already started !!!")
return
Expand Down Expand Up @@ -367,9 +367,9 @@ def run_server(self, host='0.0.0.0', port=9440): # nosec
self.server.settimeout(10)

if context:
self.logging("Status", f"WebUI Server started on SSL https://{host}:{port}")
self.logging("Log", f"WebUI Server started on SSL https://{host}:{port}")
else:
self.logging("Status", f"WebUI Server started on {host}:{port}")
self.logging("Log", f"WebUI Server started on {host}:{port}")

server_loop(self, )

Expand Down
6 changes: 6 additions & 0 deletions Classes/ZigpyTransport/AppBellows.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
#
# SPDX-License-Identifier: GPL-3.0 license

import asyncio
import logging
import time

import bellows.config as bellows_conf
import bellows.types as t
Expand Down Expand Up @@ -63,9 +65,13 @@ async def startup(self, statistics, HardwareID, pluginconf, use_of_zigpy_persist
self.captureRxFrame = captureRxFrame
self.use_of_zigpy_persistent_db = use_of_zigpy_persistent_db

self.start_time = time.time()

self.shutting_down = False
self.restarting = False

await asyncio.sleep( 3 )

try:
await self.connect()
await self.initialize(auto_form=True, force_form=force_form)
Expand Down
5 changes: 4 additions & 1 deletion Classes/ZigpyTransport/AppDeconz.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

import asyncio
import logging
import time

import zigpy.config as zigpy_conf
import zigpy.device
Expand Down Expand Up @@ -59,7 +60,9 @@ async def startup(self, statistics, HardwareID, pluginconf, use_of_zigpy_persist
self.HardwareID = HardwareID
self.captureRxFrame = captureRxFrame
self.use_of_zigpy_persistent_db = use_of_zigpy_persistent_db


self.start_time = time.time()

self.shutting_down = False
self.restarting = False

Expand Down
44 changes: 34 additions & 10 deletions Classes/ZigpyTransport/AppGeneric.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
LOGGER = logging.getLogger(__name__)

ENERGY_SCAN_WARN_THRESHOLD = 0.75 * 255
GRACE_PERIOD_AFTER_START = 60 # 60 seconds of period after plugin start to allow NCP to recover


async def _load_db(self) -> None:
Expand Down Expand Up @@ -143,9 +144,12 @@ async def shutdown(self, *, db: bool = True) -> None:
LOGGER.info("Zigpy shutdown")
self.shutting_down = True

LOGGER.info("Backup")
await _create_backup(self)

await ControllerApplication.shutdown(self, db=db)
LOGGER.info("zigpy application shutdown")
# await ControllerApplication.shutdown(self, db=db)
await super(type(self),self).shutdown( db=db)


async def _create_backup(self) -> None:
Expand All @@ -159,16 +163,35 @@ async def _create_backup(self) -> None:

def connection_lost(self, exc: Exception) -> None:
"""Handle connection lost event."""
LOGGER.warning( "+ Connection to the radio was lost (trying to recover): %s %r" %(type(exc), exc) )

super(type(self),self).connection_lost(exc)
from bellows.ash import NcpFailure # pylint: disable=import-outside-toplevel
from bellows.types.named import NcpResetCode # pylint: disable=import-outside-toplevel

if not self.shutting_down and not self.restarting and isinstance( exc, (serial.serialutil.SerialException, TimeoutError)):
LOGGER.error( "++++++++++++++++++++++ Connection to coordinator failed on Serial, let's restart the plugin")
LOGGER.warning( f"--> : self.shutting_down: {self.shutting_down}, {self.restarting}")
LOGGER.warning("+ Connection to the radio was lost: %s %r", type(exc), exc)

self.restarting = True
self.callBackRestartPlugin()
if self.shutting_down or self.restarting:
LOGGER.warning("+ shutdown or restart in progress")
return

if isinstance(exc, NcpFailure) and exc.code == NcpResetCode.ERROR_EXCEEDED_MAXIMUM_ACK_TIMEOUT_COUNT:
# it seems that the NCP is stuck, but during a plugin restart, it usally recovery, so let's give a grace period
if time.time() < (self.start_time + GRACE_PERIOD_AFTER_START):
LOGGER.warning("+ Connection to the radio was lost, give time for recover ...")
return

connection_lost_error( self, "NCP reset due to exceeded maximum ACK timeout count, plugin restart required" )

elif isinstance(exc, (serial.serialutil.SerialException, asyncio.CancelledError)):
connection_lost_error( self, "Connection to coordinator lost, SerialError or CancelledError, plugin restart required" )

elif isinstance(exc, (TimeoutError, asyncio.TimeoutError)):
connection_lost_error( self, "Connection to coordinator lost, TimeOut, plugin restart required" )


def connection_lost_error(self, message: str) -> None:
self.restarting = True
LOGGER.error(message)
self.callBackRestartPlugin()


def _retreive_previous_backup(self):
Expand Down Expand Up @@ -361,8 +384,9 @@ def packet_received(
self.log.logging("TransportZigpy", "Debug", "packet_received Sender: %s frame for plugin: %s" % (sender, plugin_frame))
self.callBackFunction(plugin_frame)

if cluster == 0x0019:
# Do not forward message to zigpy, it will be managed by Z4D plugin
if profile == 0x0104 and sender != "0000":
# ZCL Message sent by a device to the coordinator.
# Leave the answer to the plugin and not zigpy layer
return

super(type(self),self).packet_received(packet)
Expand Down
6 changes: 6 additions & 0 deletions Classes/ZigpyTransport/AppZnp.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
#
# SPDX-License-Identifier: GPL-3.0 license

import asyncio
import logging
import time

import zigpy.config as zigpy_conf
import zigpy.device
Expand Down Expand Up @@ -64,9 +66,13 @@ async def startup(self, statistics, HardwareID, pluginconf, use_of_zigpy_persist
self.captureRxFrame = captureRxFrame
self.use_of_zigpy_persistent_db = use_of_zigpy_persistent_db

self.start_time = time.time()

self.shutting_down = False
self.restarting = False

await asyncio.sleep( 3 )

# Pipiche : 24-Oct-2022 Disabling CONF_MAX_CONCURRENT_REQUESTS so the default will be used ( 16 )
# self.znp_config[znp_conf.CONF_MAX_CONCURRENT_REQUESTS] = 2

Expand Down
6 changes: 5 additions & 1 deletion Classes/ZigpyTransport/zigpyThread.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ def zigpy_thread_function(self):
if self.pluginconf.pluginConf.get("EventLoopInstrumentation", False):
zigpy_loop.set_debug(True)

self.log.logging("TransportZigpy", "Log", f"zigpyThread EventLoop: {zigpy_loop}")
self.log.logging("TransportZigpy", "Debug", f"zigpyThread EventLoop: {zigpy_loop}")

try:
# Run the Zigpy task asynchronously
Expand Down Expand Up @@ -178,12 +178,16 @@ async def start_zigpy_task(self, channel, extended_pan_id):

# We exit the worker_loop, shutdown time
try:
self.log.logging("TransportZigpy", "Log", "Shutding down zigpy thread")
await self.app.shutdown()

except Exception as e:
self.log.logging("TransportZigpy", "Error", f"start_zigpy_task shutdown(self) error: {e}")
self.log.logging("TransportZigpy", "Error", f" {str(traceback.format_exc())}")

self.log.logging("TransportZigpy", "Log", "Disconnecting communication")
await self.app.disconnect()

#await asyncio.gather(task, return_exceptions=False)
await asyncio.sleep(1)

Expand Down
4 changes: 2 additions & 2 deletions Conf/ZclDefinitions/0001.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"ClusterId": "0001",
"Description": "Power",
"Version": "1",
"Version": "1.2",
"Enabled": true,
"Attributes": {
"0000": {
Expand Down Expand Up @@ -89,7 +89,7 @@
"Acc": "RP" ,
"Default": "0",
"Mandatory": false,
"ActionList": [ "check_store_value", "upd_domo_device", "update_battery", "update_battery_percentage"]
"ActionList": [ "check_store_value", "update_battery", "update_battery_percentage"]
},

"0030": { "Enabled": true, "Name": "BatteryManufacturer", "DataType": "41", "ActionList": [ "check_store_value"] },
Expand Down
2 changes: 1 addition & 1 deletion Modules/inRawAps.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def inRawAps( self, Devices, srcnwkid, srcep, cluster, dstnwkid, dstep, Sqn, Glo
self.log.logging( "inRawAPS", "Debug", "inRawAps Nwkid: %s Ep: %s Cluster: %s ManufCode: %s Cmd: %s Data: %s" % (
srcnwkid, srcep, cluster, ManufacturerCode, Command, Data), srcnwkid, )

model_name = self.ListOfDevices[srcnwkid]["Model"] if "Model" in self.ListOfDevices[srcnwkid] else ""
model_name = self.ListOfDevices[srcnwkid].get("Model", "")

if cluster == "0020": # Poll Control ( Not implemented in firmware )
# self.log.logging("inRawAPS","Log","Cluster 0020 -- POLL CLUSTER")
Expand Down
Loading
Loading