Désolé, tellement un temps pourri que je ne l’ai pas encore sorti ,. Dès que je remets en route je te fais un feedback
À+
bonjour, j’ai eu le meme soucis, lors de la mise a jour des packages python et cela venait du changement de nom du module en provenance du git qui est devenu « aio-intex-spa ».
Avec la commande « pip freeze » tu auras la liste des modules python installés et tu pourras vérifier si la mise a jour a changé le nom du module.
Si il y a eu changement de nom du module le plus simple est de retélechargé les fichiers « example » depuis le git.
Merci de ces infos. En effet il a changé de nom. Il faut changer le script et scenario?
Mon niveau est faible.
Est ce que tu pourrais partager ce que tu as dans le script et scénario?
bonjour, non pas besoin de changer les scripts ni les scénarios, il te suffit juste de telecharger les fichier sur le git et de les remplacer dans le repertoire script/data/spa que tu as créé en suivant le tuto en debut de ce post.
edit: En revanche lors de la mise en place des nouveaux scripts python, j’ai eu des messages d’erreurs et j’ai effectué des modifs sur les fichiers en erreurs, mais je ne sais plus lesquels,lorsque tu seras arrivé à ce stade je pourrais t’orienter pour les modifs
Merci pour ton retour.
J’ai effectué la manip que tu m’indique mais ça ne marche pas J’ai cette erreur :
[2024-08-17 14:20:51] ERROR : Erreur exécution de la commande [Energie éclairage][Spa][intex_spa_set_bubble_off] : Erreur sur sudo chmod +x python3 2>/dev/null;python3 /var/www/html/plugins/script/data/spa/intex_spa_set_bubbles_off.PY 2>&1 valeur retournée : 1. Détails : Traceback (most recent call last): File "/var/www/html/plugins/script/data/spa/intex_spa_set_bubbles_off.PY", line 7, in <module> from aio_intex_spa import IntexSpa File "/usr/local/lib/python3.7/dist-packages/aio_intex_spa/__init__.py", line 3, in <module> from aio_intex_spa.intex_spa import IntexSpa File "/usr/local/lib/python3.7/dist-packages/aio_intex_spa/intex_spa.py", line 8, in <module> from .intex_spa_object_status import IntexSpaStatus File "/usr/local/lib/python3.7/dist-packages/aio_intex_spa/intex_spa_object_status.py", line 8, in <module> class IntexSpaStatus: File "/usr/local/lib/python3.7/dist-packages/aio_intex_spa/intex_spa_object_status.py", line 70, in IntexSpaStatus def current_temp(self) -> int | bool: TypeError: unsupported operand type(s) for |: 'type' and 'type'
Bonsoir, c’est les erreurs dont je te parlais et il faut modifié en SSH le fichier indiqué « /usr/local/lib/python3.7/dist-packages/aio_intex_spa/intex_spa_object_status.py » en ajoutant
from typing import Union
au début du script python et en modifiant à la ligne indiquée
int | bool
par
Union[int, bool]
moi pour etre sur j’ai modifié toutes les lignes contenant int | bool
Une fois le fichier modifié tu auras un autre fichier avec la même erreur et il faudra faire exactement la même chose.
Merci pour ton retour. Malheureusement ça depasse mes compétences
J’abuse si je demande des captures d’écran?
Pas de soucis mais il n’y a rien de compliqué.
Ouvrir le fichier cité avec la commande nano et faire les modifs indiquées au post précédent
et
"""Load IntexSpaStatus."""
import logging
_LOGGER = logging.getLogger(__name__)
from typing import Union
class IntexSpaStatus:
"""Hold and expose the status response objects.
Attributes
----------
_raw_status : int
The raw integer-encoded status data, as received from the spa
power : bool
filter : bool
heater : bool
jets : bool
bubbles : bool
sanitizer : bool
unit : str
current_temp : int
preset_temp : int
"""
@property
def power(self) -> bool:
"""Power state of the spa."""
return bool((self._raw_status >> 104) & 0b1)
@property
def filter(self) -> bool:
"""State of the filter function."""
return bool((self._raw_status >> 105) & 0b1)
@property
def heater(self) -> bool:
"""State of the heater function."""
return bool((self._raw_status >> 106) & 0b1)
@property
def jets(self) -> bool:
"""State of the jets function."""
return bool((self._raw_status >> 107) & 0b1)
@property
def bubbles(self) -> bool:
"""State of the bubbles function."""
return bool((self._raw_status >> 108) & 0b1)
@property
def sanitizer(self) -> bool:
"""State of sanitizer function."""
return bool((self._raw_status >> 109) & 0b1)
@property
def unit(self) -> str:
"""Unit of the temperature values.
* "°C" for Celsius*
* "°F" for Farenheit*
"""
if self.preset_temp <= 40:
return "°C"
else:
return "°F"
@property
def current_temp(self) -> Union[int, bool]:
"""Current temperature of the water, expressed in `unit`."""
raw_current_temp = (self._raw_status >> 88) & 0xFF
# If current_temp encodes a temperature, return the temperature
if raw_current_temp < 181:
return raw_current_temp
# Else if current_temp encodes an error (E81, ...), return False
else:
return False
@property
def error_code(self) -> Union[int, bool]:
"""Current error code of the spa."""
raw_current_temp = (self._raw_status >> 88) & 0xFF
# If current_temp encodes an error (E81, ...), return the error code
if raw_current_temp >= 181:
error_no = raw_current_temp - 100
return f"E{error_no}"
# Else if current_temp encodes a temperature, return False
else:
return False
@property
def preset_temp(self) -> int:
"""Preset temperature of the water, expressed in `unit`."""
return (self._raw_status >> 24) & 0xFF
def __init__(self, raw_status: int = None):
"""Initialize IntexSpaStatus class.
Parameters
----------
raw_status : int, optional
The raw response data received from the spa
"""
if raw_status is not None:
self.update(raw_status)
def update(self, raw_status: int):
"""Update the status of the spa from the received raw response.
Parameters
----------
raw_status : int
The raw response data received from the spa
"""
self._raw_status = raw_status
_LOGGER.debug("Spa status: '%s'", self)
def as_dict(self) -> dict:
"""Return main status attributes only, as dict.
Returns
-------
status_attributes : dict
IntexSpaStatus main status attributes as dict
"""
try:
return {
"power": self.power,
"filter": self.filter,
"heater": self.heater,
"jets": self.jets,
"bubbles": self.bubbles,
"sanitizer": self.sanitizer,
"unit": self.unit,
"current_temp": self.current_temp,
"preset_temp": self.preset_temp,
"error_code": self.error_code,
}
# If _raw_status is not defined
except AttributeError:
return {
"power": None,
"filter": None,
"heater": None,
"jets": None,
"bubbles": None,
"sanitizer": None,
"unit": None,
"current_temp": None,
"preset_temp": None,
"error_code": None,
}
def __repr__(self) -> str:
"""Represent IntexSpaStatus main attributes."""
return repr(self.as_dict())
Voici mon fichier modifié, tu as juste à faire un copier-coller
Merci pour ta patience. J’ai réussi dans ce fichier.
Maintenant j’ai cette erreur, je suis allé faire la même chose dans ce fichier mais ça ne change rien.
Erreur sur sudo chmod +x python3 2>/dev/null;python3 /var/www/html/plugins/script/data/spa/intex_spa_set_heater_on.PY 2>&1 valeur retournée : 1. Détails : Traceback (most recent call last): File « /var/www/html/plugins/script/data/spa/intex_spa_set_heater_on.PY », line 7, in from aio_intex_spa import IntexSpa File « /usr/local/lib/python3.7/dist-packages/aio_intex_spa/init.py », line 3, in from aio_intex_spa.intex_spa import IntexSpa File « /usr/local/lib/python3.7/dist-packages/aio_intex_spa/intex_spa.py », line 15, in class IntexSpa: File « /usr/local/lib/python3.7/dist-packages/aio_intex_spa/intex_spa.py », line 50, in IntexSpa self, intent: str = « status », expected_state: Union[int, bool] = None NameError: name ‹ Union › is not defined
je te joins le contenu avec la modif fonctionnelle, tu as du oublier de déclarer la fonction union au debut du code
"""Load IntexSpa class."""
import logging
import asyncio
from .intex_spa_network_layer import IntexSpaNetworkLayer
from .intex_spa_query import IntexSpaQuery
from .intex_spa_object_status import IntexSpaStatus
from .intex_spa_object_info import IntexSpaInfo
from .intex_spa_exceptions import IntexSpaUnreachableException
from typing import Union
_LOGGER = logging.getLogger(__name__)
class IntexSpa:
"""Interface the user with Intex Spa.
AsyncIO-enabled class to interface with Intex Spa wifi module
Attributes
----------
network : IntexSpaNetworkLayer
The network layer object for communications with intex spa wifi module
status : IntexSpaStatus
The status object of the spa
info : IntexSpaInfo
The info object of the spa
"""
def __init__(self, address: str = "SPA_DEVICE", port: str = "8990"):
"""Initialize IntexSpa object instance.
Parameters
----------
address : str, default = "SPA_DEVICE"
The fqdn or IP of the intex spa wifi module
port : str, default = "8990"
The TCP service port the intex spa wifi module
"""
_LOGGER.info("Initializing IntexSpa instance...")
self.network = IntexSpaNetworkLayer(address, port)
self._semaphore = asyncio.Semaphore(1)
self.status = IntexSpaStatus()
self.info = IntexSpaInfo()
_LOGGER.info("IntexSpa instance initialized")
async def _async_handle_intent(
self, intent: str = "status", expected_state: Union[bool, int] = None
) -> IntexSpaStatus:
"""Handle any intent by conversing with the spa wifi module.
An intent can be:
* update: to refresh the status object of the spa
* *command*: to change the function state or temperature preset on the spa
A conversation with the spa is made of one or more queries:
* A command intent always triggers a preliminary status update
* Command is sent and response is received
* Retries can happen as needed
Parameters
----------
intent : str
The intent to handle (i.e.: "status", "heater", "preset_temp", ...)
expected_state : bool | int, optional
The expected state of the function or the temperature preset
Returns
-------
status : IntexSpaStatus
The status of the spa
"""
_LOGGER.debug("'%s' intent: Handling new intent...", intent)
# Trigger a preliminary update status intent if the provided intent is a command
if intent != "status" and intent != "info":
_LOGGER.debug(
"'%s' intent: triggering a preliminary 'update' intent...", intent
)
await self.async_update_status()
# Run concurrent requests senquentially
async with self._semaphore:
if (
# the provided intent is an update status
intent == "status"
# the provided intent is an update info
or intent == "info"
# the provided intent is a command
# and its expected_state differs from the current state
or getattr(self.status, intent) != expected_state
):
_LOGGER.debug("'%s' intent: a spa query is needed", intent)
# Attempt maximum 3 times
for _ in range(3):
try:
_LOGGER.debug("'%s' intent: new spa query...", intent)
# Initialize a query to the spa
query = IntexSpaQuery(intent, expected_state)
# Send the raw request bytes via the network object
await self.network.async_send(query.request_bytes)
# Receive the raw response bytes via the network object
received_bytes = await self.network.async_receive()
# Give the raw received_bytes back to the query object
# to render the new status
query.render_response_data(received_bytes)
if intent == "info":
# Update the info object with the data received
self.info = IntexSpaInfo(query.response_data)
_LOGGER.debug("'%s' intent: new info is rendered", intent)
return self.info
else:
# Update the status object with the data received
self.status = IntexSpaStatus(query.response_data)
_LOGGER.debug("'%s' intent: new status is rendered", intent)
return self.status
except (AssertionError,):
_LOGGER.info("Malformed spa response during spa querying")
await asyncio.sleep(2)
continue
except (
TimeoutError,
asyncio.IncompleteReadError,
ConnectionRefusedError,
ConnectionResetError,
ConnectionError,
OSError,
):
_LOGGER.info("Network raised an exception during spa querying")
await self.network.async_force_disconnect()
await asyncio.sleep(2)
continue
else:
break
else: # No retry has succeeded
_LOGGER.info("Spa is unreachable")
self.status = IntexSpaStatus()
raise IntexSpaUnreachableException("Spa is unreachable")
# Return a status even when getattr(self.status, intent) == expected_state
# Fixes mathieu-mp/aio-intex-spa#17
return self.status
async def async_update_status(self) -> IntexSpaStatus:
"""Update known status of the spa.
Returns
-------
status : IntexSpaStatus
The updated spa status
"""
return await self._async_handle_intent("status")
async def async_set(
self, parameter: str, expected_state: bool = True
) -> IntexSpaStatus:
"""Set specified parameter to `expected_state`.
Returns
-------
status : IntexSpaStatus
The updated spa status
"""
return await self._async_handle_intent(parameter, expected_state)
async def async_set_power(self, expected_state: bool = True) -> IntexSpaStatus:
"""Set power function to `expected_state`.
Returns
-------
status : IntexSpaStatus
The updated spa status
"""
return await self.async_set("power", expected_state)
async def async_set_filter(self, expected_state: bool = True) -> IntexSpaStatus:
"""Set filter function to `expected_state`.
Returns
-------
status : IntexSpaStatus
The updated spa status
"""
return await self.async_set("filter", expected_state)
async def async_set_heater(self, expected_state: bool = True) -> IntexSpaStatus:
"""Set heater function to `expected_state`.
Returns
-------
status : IntexSpaStatus
The updated spa status
"""
return await self.async_set("heater", expected_state)
async def async_set_jets(self, expected_state: bool = True) -> IntexSpaStatus:
"""Set jets function to `expected_state`.
Returns
-------
status : IntexSpaStatus
The updated spa status
"""
return await self.async_set("jets", expected_state)
async def async_set_bubbles(self, expected_state: bool = True) -> IntexSpaStatus:
"""Set bubbles function to `expected_state`.
Returns
-------
status : IntexSpaStatus
The updated spa status
"""
return await self.async_set("bubbles", expected_state)
async def async_set_sanitizer(self, expected_state: bool = True) -> IntexSpaStatus:
"""Set sanitizer function to `expected_state`.
Returns
-------
status : IntexSpaStatus
The updated spa status
"""
return await self.async_set("sanitizer", expected_state)
async def async_set_preset_temp(self, expected_state: int) -> IntexSpaStatus:
"""Set preset_temp function to `expected_state`.
Returns
-------
status : IntexSpaStatus
The updated spa status
"""
return await self._async_handle_intent("preset_temp", expected_state)
async def async_update_info(self) -> IntexSpaInfo:
"""Update known info from the spa.
Returns
-------
info : IntexSpaInfo
The updated spa info
"""
return await self._async_handle_intent("info")
Normalement après tout devrait être opérationnel
Hello,
Super la communication est de retablie ! Merci de ton aide !
Par contre le statut remonte de manière differente maintenant. Tu as eu la même chose?
DEBUG:asyncio:Using selector: EpollSelector INFO:aio_intex_spa.intex_spa:Initializing IntexSpa instance… INFO:aio_intex_spa.intex_spa:IntexSpa instance initialized DEBUG:aio_intex_spa.intex_spa:‹ status › intent: Handling new intent… DEBUG:aio_intex_spa.intex_spa:‹ status › intent: a spa query is needed DEBUG:aio_intex_spa.intex_spa:‹ status › intent: new spa query… INFO:aio_intex_spa.intex_spa_network_layer:Not connected to the spa, trying to connect… DEBUG:aio_intex_spa.intex_spa_network_layer:Opening TCP connection with the spa at SPA_DEVICE:8990 with asyncio… INFO:aio_intex_spa.intex_spa_network_layer:TCP connection established with the spa DEBUG:aio_intex_spa.intex_spa_network_layer:Sending bytes to the spa: b’{« data »: « 8888060FEE0F01DA », « sid »: « 17242340445149 », « type »: 1}’ DEBUG:aio_intex_spa.intex_spa_network_layer:Receiving bytes from the spa: b’{« sid »:« 17242340445149 »,« data »:« FFFF110F010000150000000080808024000024 »,« result »:« ok »,« type »:2}\n’ DEBUG:aio_intex_spa.intex_spa_object_status:Spa status: ‹ {‹ power ›: False, ‹ filter ›: False, ‹ heater ›: False, ‹ jets ›: False, ‹ bubbles ›: False, ‹ sanitizer ›: False, ‹ unit ›: ‹ °C ›, ‹ current_temp ›: 21, ‹ preset_temp ›: 36, ‹ error_code ›: False} › DEBUG:aio_intex_spa.intex_spa:‹ status › intent: new status is rendered {‹ power ›: False, ‹ filter ›: False, ‹ heater ›: False, ‹ jets ›: False, ‹ bubbles ›: False, ‹ sanitizer ›: False, ‹ unit ›: ‹ °C ›, ‹ current_temp ›: 21, ‹ preset_temp ›: 36, ‹ error_code ›: False} le 2024-08-21 11:54:04
Le souci doit être là
J’ai réussi a retrouvé les feedback des commande en modifiant le scenario de @patrice_payen.
le power est maintenant en $val2. J’ai décalé l’ensemble d’un cran. Je ne sais pas pourquoi.
Et le preset temp ne fonctionne plus mais mes faible compétence me bloquent encore.
le voici adapté avec le nouveau nom « aio_intex_spa »
"""Usage example: Set spa preset temp to 25°C."""
import os
import logging
import asyncio
import sys
from aio_intex_spa import IntexSpa
SPA_ADDRESS = os.getenv("SPA_ADDRESS") or "SPA_DEVICE"
async def example_intex_spa():
"""Example for intex_spa"""
intex_spa = IntexSpa(SPA_ADDRESS)
n = int(sys.argv[1])
print(await intex_spa.async_set_preset_temp(n))
asyncio.run(example_intex_spa())
Pour ce qui est du décalage de la valeur c’est bizarre, dans ton post précédent, on voit que la valeur power est bien la première de la liste de valeurs.
Peux-tu envoyer ce que la commande
python3 /var/www/html/plugins/script/data/spa/intex_spa_get_status.PY
retourne
Merci pour ton retour ça fonctionne pour le preset.
Voici le retour de la commande :
pi@raspberrypi:~ $ pi@192.168.1.30's password:
> Linux raspberrypi 5.10.103-v7l+ #1529 SMP Tue Mar 8 12:24:00 GMT 2022 armv7l
>
> The programs included with the Debian GNU/Linux system are free software;
> the exact distribution terms for each program are described in the
> individual files in /usr/share/doc/*/copyright.
>
> Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
> permitted by applicable law.
> Last login: Thu Aug 22 14:46:51 2024 from 192.168.1.132
>
> Wi-Fi is currently blocked by rfkill.
> Use raspi-config to set the country before use.
>
> pi@raspberrypi:~ $ python3 /var/www/html/plugins/script/data/spa/intex_spa_get_status.PY
> INFO:aio_intex_spa.intex_spa:Initializing IntexSpa instance...
> INFO:aio_intex_spa.intex_spa:IntexSpa instance initialized
> INFO:aio_intex_spa.intex_spa_network_layer:Not connected to the spa, trying to connect...
> INFO:aio_intex_spa.intex_spa_network_layer:TCP connection established with the spa
> {'power': True, 'filter': False, 'heater': False, 'jets': False, 'bubbles': False, 'sanitizer': False, 'unit': '°C', 'current_temp': 29, 'preset_temp': 38, 'error_code': False}
> pi@raspberrypi:~ $
Et voila le scenario pour que ça fonctionne :
// Get data using status script
$cmdinfo = "#[Energie éclairage][Spa][spa_statut]#";
$RetourInfo = cmd::byString($cmdinfo)->execCmd();
$scenario->setLog("Retour Info : " . $retourInfo);
// Get indidual data
list($val1, $val2,$val3,$val4,$val5,$val6,$val7,$val8,$val9,$val10) = explode(',', $RetourInfo);
// Set variables
$scenario->setData("spa_inconnu", substr($val1,strrpos($val1,": ")+2,));
$scenario->setData("spa_power", substr($val2,strrpos($val2,": ")+2,));
$scenario->setData("spa_filter", substr($val3,strrpos($val3,": ")+2,));
$scenario->setData("spa_heater", substr($val4,strrpos($val4,": ")+2,));
$scenario->setData("spa_jet", substr($val5,strrpos($val5,": ")+2,));
$scenario->setData("spa_bubble", substr($val6,strrpos($val6,": ")+2,));
$scenario->setData("spa_sanitizer", substr($val7,strrpos($val7,": ")+2,));
$scenario->setData("spa_unit", substr($val8,strrpos($val8,": ")+2,));
$scenario->setData("spa_current_temp", substr($val9,strrpos($val9,": ")+2,));
$scenario->setData("spa_preset_temp", substr($val10,strrpos($val10,": ")+2,));
$pos = strrpos($val10,": ");
$len = strlen($val10)-$pos-3;
$sub = substr($val10,$pos+2,$len);
$scenario->setData("spa_error", $sub);
// Show Variables
$scenario->setLog($scenario->getData("spa_inconnu"));
$scenario->setLog($scenario->getData("spa_power"));
$scenario->setLog($scenario->getData("spa_filter"));
$scenario->setLog($scenario->getData("spa_heater"));
$scenario->setLog($scenario->getData("spa_jet"));
$scenario->setLog($scenario->getData("spa_bubble"));
$scenario->setLog($scenario->getData("spa_sanitizer"));
$scenario->setLog($scenario->getData("spa_unit"));
$scenario->setLog($scenario->getData("spa_current_temp"));
$scenario->setLog($scenario->getData("spa_preset_temp"));
bizarre la valeur power est bien en première position.
Une petite question en passant, tu as bien remplacé les fichiers PY dans script/data/spa par les nouveaux sur le github?
Je viens de revérifier et c’est bien les nouveaux en from intex_spa.intex_spa.
On m’appel le chat noir
Normalement les nouveaux ont
from aio_intex_spa import IntexSpa
en debut de code.
Si jamais tu ne les as pas
Tu pourras les retélécharger ici fichier aio_intex