SONOFF NSPanel - qui a testé?

Superbe disgn, pour l’instant moi je reste sur du moche pour tester.
Pour remonter des informations il faut passer l’ecran en « mode 1 » et après on peux publier directement les instructions sur « NSPpanel2 ».

Par exemple je publie sur mqtt:

  • cmnd/nspanel_7BDF98/NSPMode 1

  • cmnd/nspanel_7BDF98/NSPSend2 t0.txt= ''hello"

  • cmnd/nspanel_7BDF98/NSPSend2 n1.val=19

Pour éviter de perdre l’information lors d’un changement de page, j’ai lu qu’il fallait passer le « vscope » de l’objet sur global.

Pour avoir l’heure je cherche encore, j’ai une piste ici:
https://www.letscontrolit.com/wiki/index.php/NEXTIONDisplay

A mon avis, il faut modifier le fichier nspanel.be pour que tasmota envoie régulièrement l’information.

Maintenant que l’upload est fonctionnel. Je vais me pencher de plus prêt sur tout ca. Je posterai également ici ce que je trouve comme info

2 « J'aime »

Super tu avances bien, je vais lire tes liens et tenter cet upload ce soir :wink:

Je viens de modifier (dans la mesure de mes capacités n’étant pas programmeur) le fichier .be pour ne plus avoir besoin de passer l’ecran en mode 1 et recevoir de manière clair pour jeedom sur le canal

tele/nspanel_7BDF98/RESULT{Nextion}

Pour rappel l’envoie d’ordre se fait sur :

cmnd/nspanel_7BDF98/NSPSend2

l’idéal serait de pouvoir envoyer un json avec l’ensemble des ordres mais j’ai pas réussi. :unamused:

Si vous pouvez l’améliorer, votre bon coeur :grinning_face_with_smiling_eyes:

# Sonoff NSPanel Tasmota driver for custom nextion
# based on;
# Sonoff NSPanel Tasmota driver v0.47 | code by blakadder and s-hadinger

var mode = "Nextion"
import persist
var devicename = tasmota.cmd("DeviceName")["DeviceName"]
persist.tempunit = tasmota.get_option(8) == 1 ? "F" : "C"
if persist.has("dim")  else   persist.dim = "1"  end
var loc = persist.has("loc") ? persist.loc : "North Pole"       
persist.save() # save persist file until serial bug fixed


class NSPanel : Driver
 
  static header = bytes('55AA') 

  var ser  # create serial port object
       
  # intialize the serial port, if unspecified Tx/Rx are GPIO 16/17
  def init(tx, rx)
    if !tx   tx = 16 end
    if !rx   rx = 17 end
    self.ser = serial(rx, tx, 115200, serial.SERIAL_8N1)
    tasmota.add_driver(self)
  end

  # determine type of message
  def findtype(value)
    import string
    for k:self.types.keys()
      if string.find(value, k) >= 0
        return self.types[k]
      end
    end
    return 0
  end

  def crc16(data, poly)
    if !poly  poly = 0xA001 end
    # CRC-16 MODBUS HASHING ALGORITHM
    var crc = 0xFFFF
    for i:0..size(data)-1
      crc = crc ^ data[i]
      for j:0..7
        if crc & 1
          crc = (crc >> 1) ^ poly
        else
          crc = crc >> 1
        end
      end
    end
    return crc
  end

  # encode using NSPanel protocol
  # input: payload:json string
  def encode(payload)
    var b = bytes()
    var nsp_type = self.findtype(payload)
    b += self.header
    b.add(nsp_type)       # add a single byte
    b.add(size(payload), 2)   # add size as 2 bytes, little endian
    b += bytes().fromstring(payload)
    var msg_crc = self.crc16(b)
    b.add(msg_crc, 2)       # crc 2 bytes, little endian
    return b
  end

  def split_55(b)
    var ret = []
    var s = size(b)   
    var i = s-1   # start from last
    while i > 0
      if b[i] == 0x55 && b[i+1] == 0xAA           
        ret.push(b[i..s-1]) # push last msg to list
        b = b[(0..i-1)]   # write the rest back to b
      end
      i -= 1
    end
    ret.push(b)
    return ret
  end

  # send a string payload (needs to be a valid json string)
  def send(payload)
    print("NSP: Sent =", payload)
    var payload_bin = self.encode(payload)
    self.ser.write(payload_bin)
    # print("NSP: Sent =", payload)
    log("NSP: NSPanel payload sent = " + str(payload_bin), 3)
  end

  # send a nextion payload
  def encodenx(payload)
    var b = bytes().fromstring(payload)
    b += bytes('FFFFFF')
    return b
  end

  def sendnx(payload)
    var payload_bin = self.encodenx(payload)
    self.ser.write(payload_bin)
    # print("NSP: Sent =", payload_bin)
    log("NSP: Nextion command sent = " + str(payload_bin), 3)
  end

  # sets time and date according to Tasmota local time
  def set_clock()
    var now = tasmota.rtc()
    var time_raw = now['local']
    var nsp_time = tasmota.time_dump(time_raw)
    var time_payload = '{"year":' + str(nsp_time['year']) + ',"mon":' + str(nsp_time['month']) + ',"day":' + str(nsp_time['day']) + ',"hour":' + str(nsp_time['hour']) + ',"min":' + str(nsp_time['min']) + ',"week":' + str(nsp_time['weekday']) + '}'
    log('NSP: Time and date synced with ' + time_payload, 3)
    self.send(time_payload)
  end

  # sync main screen power bars with tasmota POWER status
  def set_power()
    var ps = tasmota.get_power()
    for i:0..1
      if ps[i] == true
        ps[i] = "on"
      else 
        ps[i] = "off"
      end
    end
    var json_payload = '{\"switches\":[{\"outlet\":0,\"switch\":\"' + ps[0] + '\"},{\"outlet\":1,\"switch\":\"' + ps[1] +  '\"}]}'
    log('NSP: Switch state updated with ' + json_payload)
    self.send(json_payload)
  end  



  # commands to populate an empty screen, should be executed when screen initializes
  def screeninit()
    # self.send('{"queryInfo":"version"}')
    self.send('{"HMI_ATCDevice":{"ctype":"device","id":"' + self.atc['id'] + '","outlet":' + self.atc['outlet'] + ',"etype":"' + self.atc['etype'] + '"}')
    self.send('{"relation":[{"ctype":"device","id":"panel","name":"' + devicename + '","online":true}]}')
    self.send('{"HMI_dimOpen":' + persist.dim + '}')
    self.set_clock()
    tasmota.cmd("State")
    tasmota.cmd("TelePeriod")
  end

  # read serial port and decode messages according to protocol used
  def every_100ms()
    if self.ser.available() > 0
    var msg = self.ser.read()   # read bytes from serial as bytes
    import string
      if size(msg) > 0
        print("NSP: Received Raw =", msg)
        if msg[0..1] == self.header
          #mode = "NSPanel"
          var lst = self.split_55(msg)
          for i:0..size(lst)-1
            msg = lst[i]
              if self.atc['mirror'] == true
                if msg[2] == 0x84 self.ser.write(msg)   # resend messages with type 0x84 for thermostat page
                end
              end
            var j = size(msg) - 1
            while msg[j] != 0x7D
              msg = msg[0..-1]
              j -= 1
            end        
            msg = msg[5..j]
              if size(msg) > 2
                if msg == bytes('7B226572726F72223A307D') # don't publish {"error":0}
                else 
                var jm = string.format("{\"NSPanel\":%s}",msg.asstring())
                tasmota.publish_result(jm, "RESULT")
                end
              end
          end
        elif msg == bytes('000000FFFFFF88FFFFFF')
          log("NSP: Screen Initialized")   # print the message as string
          self.screeninit()
        else
          #var jm = string.format("{\"NSPanel\":{\"Nextion\":\"%s\"}}",str(msg[0..-4]))
          var jm = string.format("{\"Nextion\":\"%s\"}",msg.asstring()) #message pour Jeedom
          tasmota.publish_result(jm, "RESULT")        end       
      end
    end
  end
end      

nsp=NSPanel()

tasmota.add_rule("power1#state", /-> nsp.set_power())
tasmota.add_rule("power2#state", /-> nsp.set_power())

# add NSPSend command to Tasmota
def nspsend(cmd, idx, payload, payload_json)
  # NSPSend2 sends Nextion commands
  if idx == 2
  var command = nsp.sendnx(payload)
  tasmota.resp_cmnd_done()
  # NSPSend sends NSPanel commands, requires valid payload
  else
  import json
  var command = nsp.send(json.dump(payload_json))
  tasmota.resp_cmnd_done()
  end
end

tasmota.add_cmd('NSPSend', nspsend)

# add NSPMode command to Tasmota
def modeselect(NSPMode, idx, payload)
  if payload == "1"
    nsp.sendnx('DRAKJHSUYDGBNCJHGJKSHBDN')
    tasmota.resp_cmnd_done()
    mode = "Nextion"
  elif payload == "0"
    nsp.sendnx('recmod=1')
    nsp.sendnx('recmod=1')
    mode = "NSPanel"
    tasmota.resp_cmnd_done()
  else
  tasmota.resp_cmnd_str('{"Mode":"' + mode + '"}')
  end
end

tasmota.add_cmd('NSPMode', modeselect)

# add NSPDim command to Tasmota
def dimopen(NSPDim, idx, payload)
  if payload == "0" || payload == "1"
    persist.dim = payload
    nsp.send('{"HMI_dimOpen":' + payload + '}')
    tasmota.resp_cmnd_done()
  else
    payload = str(persist.dim)
  end
  import string
  var jm = string.format("{\"NSPanel\":{\"Energy-saving\":%s}}",payload)
  tasmota.publish_result(jm, "RESULT")
end

tasmota.add_cmd('NSPDim', dimopen)

# add NSPLocation command to Tasmota
def setloc(NSPLocation, idx, payload)
  if size(payload) > 1
    persist.loc = payload
    tasmota.resp_cmnd_done()
    persist.save()
    loc = persist.loc
    nsp.set_weather()
  else
    payload = loc
  end
  import string
  var jm = string.format("{\"NSPanel\":{\"Location\":\"%s\"}}",payload)
  tasmota.publish_result(jm, "RESULT")
end

tasmota.add_cmd('NSPLocation', setloc)

# set displayed indoor temperature to value:int
def set_temp(value)
  var temp_payload = '{"temperature":' + str(value) + ',"tempUnit":"' + persist.tempunit + '"}'
  log('NSP: Indoor temperature set with ' + temp_payload, 3)
  nsp.send(temp_payload)
end

tasmota.add_rule("Tele#ANALOG#Temperature1", set_temp) # rule to run set_temp on teleperiod

# set wifi icon status

def set_wifi(value)
  var rssi = (value-1)/20
  rssi = '{"wifiState":"connected","rssiLevel":' + str(rssi) + '}'
  log('NSP: Wi-Fi icon set with ' + rssi, 3)
  nsp.send(rssi)
end

def set_disconnect()
  nsp.send('{"wifiState":"nonetwork","rssiLevel":0}')
end

def sync_weather() # set weather every 60 minutes
  nsp.set_weather()
  print("Weather forecast synced")
  tasmota.set_timer(60*60*1000, sync_weather)
end

tasmota.cmd("Rule3 1") # needed until Berry bug fixed
tasmota.cmd("State")
tasmota.add_rule("Time#Minute", /-> nsp.set_clock()) # set rule to update clock every minute
tasmota.add_rule("Tele#Wifi#RSSI", set_wifi) # set rule to update wifi icon
tasmota.add_rule("wifi#disconnected", set_disconnect) # set rule to change wifi icon on disconnect
tasmota.add_rule("mqtt#disconnected", set_disconnect) # set rule to change wifi icon on disconnect
tasmota.add_rule("system#boot", /-> nsp.screeninit()) 
tasmota.add_rule("time#initialized", sync_weather)

tasmota.cmd("TelePeriod")


nsp.sendnx('DRAKJHSUYDGBNCJHGJKSHBDN')
tasmota.resp_cmnd_done()
1 « J'aime »

Salut de mon coté je progresse aussi :
J’ai programmer les boutons comme ceci :
pour l’alarme avec commande on, off ou night
image
ou pour les zone de lumières avec les commandes Upoff et Downoff
image
Ce qui permet de recevoir ca sur le serveur mqtt
image
et donc je récupéré ca sous Jeedom comme ceci :


Plus qu’a associé les scénarios.
J’ai également pu programmer sur le nextion un écran de veille.
Me reste a arriver a afficher heure / date et température.

1 « J'aime »

Pourquoi « prints » plutot que « print » tout court ?
Pour la température moi j’ai mis un Xfloat en global sur la première page que je modifie depuis jeedom

1 « J'aime »

Très bonne question… j’ai lu sur un post qu’il fallait utiliser celle ci. la raison… aucune idée

Si je me fie à la doc il dise que la commande print tout court est dépréciée.

Comment tu fais du coup pour le xfloat ?

Comme j’ai pas compris la nuance je suis parti au plus simple. Les deux commandes sont expliqué ici

J’aime beaucoup le retour en json, je n’y avais pas pensé. C’est une très bonne idée

Dans mon disgn j’ai ajouté un Sans titre1 nommé x0 que je passe en global pour qu’il ne perde pas sa valeur au changement de page.

Après j’ai juste à poster ma valeur x10 sur mqtt :

1 « J'aime »

Ok je vais tester ca.

La ou je bute moi, c’est mon sénario qui traite l’information sur RESULT ne se lance que si RESULT change. Du coup si j’appui deux fois sur le même bouton ca ne marche pas comme il envoie deux fois la même valeur.

Répéter les valeurs identiques dans la configuration de la commande info. ca va t’aider :slight_smile:

une heure que je cherche :star_struck: merci c’est top :+1:

Par contre tu travaille avec le nspanel.be et pas le nxpanel.be?
Car moi la commande NSPSend2 ne marche pas, si je repasse sur le nspanel.be les commandes que j’ai défini n’arrivent pas sous le bon format. :unamused:

1 « J'aime »

Utilise la version modifié que j’ai posté, elle simplifie les choses. Sinon il faut constamment passer le nspanel en mode écriture.
L’envoie de commande ne marche pas avec le nxpanel

1 « J'aime »

OK je regarderai ca, le risque c’est que ca ne prennent pas en compte mes commandes sous le bon format.
Sinon si je comprend bien dans ce fichier tu as forcer le mode 2 pour l’envoie des commandes ?

Tu fais comme moi tu utilise le nxpanel.be pour upload les tft et tu repasse sur le nspanel.be pour les commandes en modifiant l’autoexe.be

1 « J'aime »

J’ai mis les deux fichier dedans. Mon autoexec.be ressemble à ça :

load("perso.be")
#load("nxpanel.be")

Et je change le # pour charger ou non le bon fichier.
Le repasse en permance en mode 0 ce qui est relou. j’ai donc suprimé les lignes inutiles et reformaté la sortie RESULT

1 « J'aime »