Brosse à dents Oral B

Bonjour,

Du temps du plugin-blea, j’avais intégré ma brosse à dents dans celui-ci :

Résumé
from bluepy import btle
import time
import logging
import globals
import struct
from multiconnect import Connector
from notification import Notification

class OralB():
	def __init__(self):
		self.name = 'oralb'
		self.ignoreRepeat = False
		self.characteristicHandles = {
			'battery': '0x002d'
		}

	def isvalid(self,name,manuf='',data='',mac=''):
		if manuf.lower().startswith("dc00"):
			return True

	def parse(self,data,mac,name,manuf):
		action={}
		action['present'] = 1
		logging.debug('OralB------ Parse => Manufacturer : ' + manuf )
		status = manuf[10:12]
		if status == '02':
			logging.debug('OralB------ Advertising Data => Status : ' + str(status) + ' => Brushing stopped')
			action['status'] = 0
		elif status == '03':
			logging.debug('OralB------ Advertising Data => Status : ' + str(status) + ' => Brushing in progress')
			action['status'] = 1
		pressure = int(manuf[12:14],16)
		logging.debug('OralB------ Advertising Data => Pressure : ' + str(pressure))
		action['pressure'] = pressure
		brushingDurationMin = int(manuf[14:16],16)
		brushingDurationSec = int(manuf[16:18],16)
		brushingDuration = brushingDurationMin * 60 + brushingDurationSec
		logging.debug('OralB------ Advertising Data => Brushing Duration : ' + str(brushingDuration) + ' seconde(s)')
		action['brushingDuration'] = brushingDuration
		if status == '02' and brushingDuration > 0:
			logging.debug('OralB------ Advertising Data => Previous Brushing Duration : ' + str(brushingDuration) + ' seconde(s)')
			action['previousBrushingDuration'] = brushingDuration
		mode = manuf[18:20]
		if mode == '01':
			action['mode'] = 'Propreté'
			logging.debug('OralB------ Advertising Data => Mode : ' + str(mode) + ' => Propreté')
		elif mode =='02':
			action['mode'] = 'Douceur'
			logging.debug('OralB------ Advertising Data => Mode : ' + str(mode) + ' => Douceur')
		elif mode =='03':
			action['mode'] = 'Massage'
			logging.debug('OralB------ Advertising Data => Mode : ' + str(mode) + ' => Massage')
		elif mode =='04':
			action['mode'] = 'Blanchiment'
			logging.debug('OralB------ Advertising Data => Mode : ' + str(mode) + ' => Blanchiment')
		elif mode =='05':
			action['mode'] = 'Nettoyage Approfondi'
			logging.debug('OralB------ Advertising Data => Mode : ' + str(mode) + ' => Nettoyage Approfondi')
		quadrant = int(manuf[20:22],16)
		logging.debug('OralB------ Advertising Data => Quadrant : ' + str(quadrant))
		action['quadrant'] = quadrant
		if status == '02' and brushingDuration == 0 and quadrant == 15:
			try:
				conn = Connector(mac)
				conn.connect()
				if not conn.isconnected:
					conn.connect()
					if not conn.isconnected:
						return
				battery = conn.readCharacteristic(self.characteristicHandles['battery'])
				logging.debug('OralB------ Refreshing Data => Battery Hex : ' + str(battery.hex()) + ' / Dec :' + str(int(str(battery.hex()),16)) + ' %')
				action['battery'] = int(str(battery.hex()),16)
				action['id'] = mac
				logging.debug('OralB------'+str(action))
			except Exception as e:
				logging.error(str(e))
		return action

	def read(self,mac):
		result={}
		try:
			conn = Connector(mac)
			conn.connect()
			if not conn.isconnected:
				conn.connect()
				if not conn.isconnected:
					return
			battery = conn.readCharacteristic(self.characteristicHandles['battery'])
			logging.debug('OralB------ Refreshing Data => Battery Hex : ' + str(battery.hex()) + ' / Dec :' + str(int(str(battery.hex()),16)) + ' %')
			result['battery'] = int(str(battery.hex()),16)
			result['id'] = mac
			logging.debug('OralB------'+str(result))
			return result
		except Exception as e:
			logging.error(str(e))


      
globals.COMPATIBILITY.append(OralB)

et j’utilisais le widget suivant grâce à Spine :

image

Le cadran avance en fonction de la durée qui est affichée au centre et passe en rouge si la pression est différente de 0.

Résumé
<div class="eqLogic-widget eqLogic allowResize" style="height: #height#;width: #width#;border:#border#;border-radius:#border-radius#;background-color: #background-color#;color: #color#;#style#" data-eqLogic_id="#id#" data-eqLogic_uid="#uid#" data-version="#version#">

	<span class="cmd refresh pull-right cursor" data-cmd_id="#refresh_id#">
    	<i class="fas fa-sync"></i>
	</span>

	<center class="widget-name"><a href="#eqLink#">#name_display# <span class="object_name">#object_name#</span></a></center>

	<center>
		<div class="cmd cmd-widget #status_history#" data-type="info" data-subtype="binary" data-template="tmpliconline" data-cmd_id="#status_id#" data-cmd_uid="#status_uid#" data-version="#version#" data-eqLogic_id="#id#">
			<div class="content-xs">
              <span class="cmdName #status_hide_name#"><strong>#status_name_display# </strong></span> <span class="iconCmdLine"></span>
			</div>
      </div>
	 </center>
  

	<center>
		<div class="cmd cmd-widget #previousBrushingDuration_history#" data-type="info" data-subtype="numeric" data-template="line" data-cmd_id="#previousBrushingDuration_id#" data-cmd_uid="#previousBrushingDuration_uid#" data-version="#version#" data-eqLogic_id="#id#">
			<div class="content-xs">
			<span class="cmdName #previousBrushingDuration_hide_name#"><strong>#previousBrushingDuration_name_display# </strong></span> <strong class="state"></strong> #previousBrushingDuration_unite#
			</div>
      </div>
  </center>

	<center>
		<div class="cmd cmd-widget #pressure_history#" data-type="info" data-subtype="numeric" data-template="line" data-cmd_id="#pressure_id#" data-cmd_uid="#pressure_uid#" data-version="#version#" data-eqLogic_id="#id#">
			<div class="content-xs">
			<span class="cmdName #pressure_hide_name#"><strong>#pressure_name_display# </strong></span> <strong class="state"></strong> #pressure_unite#
			</div>
      </div>
  </center>
  
	<center>
		<div class="cmd cmd-widget #brushingDuration_history#" data-type="info" data-subtype="numeric" data-template="default" data-cmd_id="#brushingDuration_id#" data-cmd_uid="#brushingDuration_uid#" data-version="#version#" data-eqLogic_id="#id#" title="Date de valeur : #brushingDuration_valueDate#<br/>Date de collecte : #brushingDuration_collectDate#" >
			<div class="content-sm">
				<div class="gaugeBrushingDuration cursor #brushingDuration_history#" data-cmd_id="#brushingDuration_id#"></div>
			</div>
		</div>
	 </center>
  
	<center>
		<div class="cmd cmd-widget #mode_history#" data-type="info" data-subtype="string" data-template="default" data-cmd_id="#mode_id#" data-cmd_uid="#mode_uid#" data-version="#version#" data-eqLogic_id="#id#">
			<div class="content-xs">
			<span class="iconCmdLine"></span> <strong class="state"></strong>
			</div>
		</div>
	 </center>

	<center>
		<div class="cmd cmd-widget #battery_history#" data-type="info" data-subtype="numeric" data-template="line" data-cmd_id="#battery_id#" data-cmd_uid="#battery_uid#" data-version="#version#" data-eqLogic_id="#id#">
			<div class="content-xs">
			<i class="fas fa-battery-full"></i> <strong class="state"></strong> #battery_unite#
			</div>
      </div>
  </center>
  
<style>
  .gaugeBrushingDuration {
    width: 120px;
    height: 120px;
  }
  .gaugeBrushingDuration .highcharts-pane {
    fill: var(--el-defaultColor);
  }
  body[data-theme="core2019_Dark"] .gaugeBrushingDuration .highcharts-tick { 
    stroke: rgb(38, 38, 38);
  }
  body[data-theme="core2019_Light"] .gaugeBrushingDuration .highcharts-tick { 
    stroke: rgb(249, 249, 250);
  }
  .gaugeBrushingDuration .highcharts-container .highcharts-axis-line {
    stroke: transparent;
  }
</style>
  
<script>
  
  	/*var brushingColor = '';
 
  	function changeColor(pressure){
      	if (pressure == 192){
			brushingColor = 'red';
        } else {
			brushingColor = 'blue'; 
        }
      console.log("Change Color : " + pressure);
      console.log("Change Color : " + brushingColor);
    }
  
  function getColor(){
    	console.log("Get Color : " + brushingColor);
      	return brushingColor;
    }*/
  
	if ('#refresh_id#' != ''){
		$('.eqLogic[data-eqLogic_uid=#uid#] .refresh').on('click', function () {
			jeedom.cmd.execute({id: '#refresh_id#'});
			});
		}else{
			$('.eqLogic[data-eqLogic_uid=#uid#] .refresh').remove();
		}
  
	jeedom.cmd.update['#status_id#'] = function(_options) {
		var cmd = $('.cmd[data-cmd_id=#status_id#]')
		cmd.attr('title','Date de valeur : '+_options.valueDate+'<br/>Date de collecte : '+_options.collectDate)
		if (_options.display_value == '0') {
			$('.cmd[data-cmd_id=#status_id#] .iconCmdLine').empty().append('<i class="fas fa-times icon_red"></i>')
		} else {
			$('.cmd[data-cmd_id=#status_id#] .iconCmdLine').empty().append('<i class="fas fa-spin fa-spinner icon_green"></i>')
		}
	}
	jeedom.cmd.update['#status_id#']({display_value:'#status#',valueDate:'#status_valueDate#',collectDate:'#status_collectDate#',alertLevel:'#status_alertLevel#'})
  
  
	jeedom.cmd.update['#mode_id#'] = function(_options) {
		var cmd = $('.cmd[data-cmd_id=#mode_id#]')
		cmd.attr('title','Date de valeur : '+_options.valueDate+'<br/>Date de collecte : '+_options.collectDate)
		if (_options.display_value == 'Propreté') {
			$('.cmd[data-cmd_id=#mode_id#] .iconCmdLine').empty().append('<i class="fas fa-tooth icon_blue"></i>')
		} else if (_options.display_value == 'Douceur') {
			$('.cmd[data-cmd_id=#mode_id#] .iconCmdLine').empty().append('<i class="fas fa-feather-alt icon_blue"></i>')
		} else if (_options.display_value == 'Massage') {
			$('.cmd[data-cmd_id=#mode_id#] .iconCmdLine').empty().append('<i class="fas fa-tooth icon_blue"></i>')
		} else if (_options.display_value == 'Blanchiment') {
			$('.cmd[data-cmd_id=#mode_id#] .iconCmdLine').empty().append('<i class="far fa-gem icon_blue"></i>')
		} else if (_options.display_value == 'Nettoyage Approfondi') {
			$('.cmd[data-cmd_id=#mode_id#] .iconCmdLine').empty().append('<i class="fas fa-tooth icon_blue"></i>')
		}
		cmd.find('.state').empty().append(_options.display_value)
	}
	jeedom.cmd.update['#mode_id#']({display_value:'#mode#',valueDate:'#mode_valueDate#',collectDate:'#mode_collectDate#',alertLevel:'#mode_alertLevel#'})

  
	jeedom.cmd.update['#previousBrushingDuration_id#'] = function(_options) {
		var cmd = $('.cmd[data-cmd_id=#previousBrushingDuration_id#]')
		cmd.attr('title','Date de valeur : '+_options.valueDate+'<br/>Date de collecte : '+_options.collectDate)
		cmd.find('.state').empty().append(_options.display_value);
	}
	jeedom.cmd.update['#previousBrushingDuration_id#']({display_value:'#previousBrushingDuration#',valueDate:'#previousBrushingDuration_valueDate#',collectDate:'#previousBrushingDuration_collectDate#',alertLevel:'#previousBrushingDuration_alertLevel#'})
  
	jeedom.cmd.update['#pressure_id#'] = function(_options) {
		var cmd = $('.cmd[data-cmd_id=#pressure_id#]')
		cmd.attr('title','Date de valeur : '+_options.valueDate+'<br/>Date de collecte : '+_options.collectDate)
		cmd.find('.state').empty().append(_options.display_value);
		/*changeColor(_options.display_value);*/
      
	}
	jeedom.cmd.update['#pressure_id#']({display_value:'#pressure#',valueDate:'#pressure_valueDate#',collectDate:'#pressure_collectDate#',alertLevel:'#pressure_alertLevel#'})
  
  
	jeedom.cmd.update['#battery_id#'] = function(_options) {
		var cmd = $('.cmd[data-cmd_id=#battery_id#]')
		cmd.attr('title','Date de valeur : '+_options.valueDate+'<br/>Date de collecte : '+_options.collectDate)
		cmd.find('.state').empty().append(_options.display_value);
	}
	jeedom.cmd.update['#battery_id#']({display_value:'#battery#',valueDate:'#battery_valueDate#',collectDate:'#battery_collectDate#',alertLevel:'#battery_alertLevel#'})
    
  
	jeedom.cmd.update['#brushingDuration_id#'] = function(_options) {
    	$('.cmd[data-cmd_id=#brushingDuration_id#]').attr('title','Date de valeur : '+_options.valueDate+'<br/>Date de collecte : '+_options.collectDate)
      	/*$('.cmd[data-cmd_uid=#brushingDuration_uid#] .gaugeBrushingDuration').highcharts().setOptions({colors: ['red']})*/
      	$('.cmd[data-cmd_uid=#brushingDuration_uid#] .gaugeBrushingDuration').highcharts().series[0].points[0].update(_options.display_value)
	}
	
  
	if (is_numeric('#brushingDuration#')) {
      /*brushingColor = getColor();*/
    $('.cmd[data-cmd_uid=#brushingDuration_uid#] .gaugeBrushingDuration').empty().highcharts({
      chart: {
        style: {
          fontFamily: 'Roboto'
        },
        type: 'solidgauge',
        plotBackgroundColor: null,
        plotBackgroundImage: null,
        backgroundColor: null,
        plotBorderWidth: 0,
        plotShadow: false,
        height: 106,
        spacingTop: 0,
        spacingLeft: 0,
        spacingRight: 0,
        spacingBottom: 0,
        borderWidth : 0
      },
      title: null,
      pane: {
        center: ['50%', '50%'],
        size: 100,
        startAngle: 0,
        endAngle: 360,
        background: {
          innerRadius: '70%',
          outerRadius: '100%',
          shape: 'arc',
          borderWidth: 0,
        }
      },
      tooltip: {
        enabled: false
      },
      // the value axis
      yAxis: {
        lineWidth: 0,
        minorTickInterval: null,
        tickInterval: 30,
        tickWidth: 4,
        tickLength: 15,
        tickPosition: 'inside',
        labels : {enabled: false},
        lineWidth: 0,
        min: 0,
        max: 120,
        zIndex: 6,
        title: {
          text: ''
        }
      },
      labels : {enabled: false},
      plotOptions: {
        solidgauge: {
          dataLabels: {
            y: 9,
            borderWidth: 0,
            useHTML: true
          },
          rounded: true
        }
      },
      credits: {
        enabled: false
      },
      exporting : {
        enabled: false
      },
      series: [
      {
        name: '',
        data: [{
          y: Math.round(parseFloat('#brushingDuration#') * 10) / 10,
/*        	color: '#pressure#' == 192 ? "red" : "blue"}],*/
        	color: '#brushingColor#'}],
        radius: '100%',
        innerRadius: '70%',
        dataLabels: {
          y: 6,
          format: '<span style="color: var(--link-color); position: relative; top: -28px; font-size: 24px">{y}</span>'
        },
      }
	]
    })
  } else {
    $('.cmd[data-cmd_uid=#brushingDuration_uid#] .gaugeBrushingDuration').append('<center><span class="label label-danger">#brushingDuration#</span></center>')
  }
</script>

</div>

Je voudrais maintenant l’utiliser sur un équipement en dehors du plugin-blea mais les widgets n’étant pas ma tasse de thé, s’il y avait une âme charitable pour m’aider à le modifier, je suis preneur.

Merci.

Bonsoir @arnog23,

Bonne année !

Merci. Je teste cela dès que je peux.

Bonne année également.

Testé et approuvé :+1:

J’ai juste modifié le startAngle (0) et endAngle(360) pour que cela commence en haut. A voir si cela est interessant de les mettre aussi en paramètres.

En revanche, y aurait-il moyen de faire changer la couleur dynamiquement en fonction d’une autre commande info de l’équipement ?

Y aurait-il également moyen de changer l’affichage de la valeur qui se trouve au centre ? Dans mon cas, il s’agit d’une durée. Un affichage du genre 0:00 serait-il possible ?

Je l’ai dupliqué en Mobile mais les couleurs ne semblent pas s’appliquer. Y-a-t-il quelquechose de particulier à faire sur la version Mobile ?

Bonsoir,

J’ai fait ça mais en relisant votre demande je m’aperçois que j’ai mal compris :
Animation

Oui mais la jauge attend du numeric pour calculer sa position pas du string.
Donc vous voulez qu’elle dépende de 3 commandes ?

  • 1 cmd numeric pour la position de la jauge
  • 1 cmd string pour la valeur affichée
  • 1 cmd (type ?) pour la couleur de remplissage
1 « J'aime »

Bonsoir,
Je vais tester cette nouvelle version dès que je peux.

Je dirais seulement deux commandes :

  • 1 cmd numeric pour la position de la gauge et l’affichage au format M:SS. Il me semblait que c’était possible de faire un calcul dans le widget avant d’afficher la valeur mais peut être que je me trompe. Peut être une option « format » à ajouter. Par défaut affichage de la valeur brute, avec l’option, affichage au format M:SS

  • 1 cmd numeric ou binaire (?) qui permet de changer la couleur des 4 quartiers. Il faut que je revérifie mais en situation normale la pression vaut 0, si on appuie trop fort ça passe à 192 (dans ce cas il faudrait pouvoir changer la couleur des 4 secteurs en rouge par exemple). Il y aussi un passage de la valeur à 32 mais je ne sais plus à quelle situation il correspond. Pas sur que la valeur 32 soit utile. Je vais revérifier.

J’ai mis à jour le code :

  • ajout des paramètres startAngle et endAngle
  • ajout d’un paramètre pour afficher des secondes au format M:SS
  • ajout d’un paramètre pour renseigner l’ID d’une 2ème commande
  • changement de la couleur de remplissage en fonction de la valeur d’une 2ème commande, orange si == 32, rouge si == 192 et couleur par défaut sinon
2 « J'aime »

Ca fonctionne nickel. Merci beaucoup.

Après vérification, le passage par la valeur 32 se fait au moment ou l’on arrête le brossage. Je verrais à l’usage si je la conserve ou pas.

Quand je rafraichie la page, je vois que le chono fait un mouvement de la droite vers gauche pour se mettre à sa position définitive. C’est un effet souhaité ?

Oui c’est pour indiquer le sens de brossage optimal. :sweat_smile:
C’est un effet de bord car je fait un innerHTML sur un span pendant qu’Highcharts génère le code donc le span est affiché avant que sa position finale ne soit définie.
Je regarde pour fixer sa position.

:joy:

Sinon, en version mobile, les couleurs des cadrans ne semblent pas fonctionner. Ils sont toujours en blanc.

Oui j’ai vu et j’ai déjà commencé à regarder (https://github.com/Spine34/jeedom-widgets/blob/master/mobile/cmd.info.numeric.solidGaugeArnog23.html), il n’y pas que la couleur qui ne va pas, il y avait une erreur qui faisait buguer tout le widget car jeeFrontEnd.jeedomVersion n’est pas défini en version mobile.
Pour la couleur c’est plus compliqué… On termine la version dashboard et on passe à la version mobile ensuite.

Bonjour,

J’ai mis à jour le code version dashboard :

  • position du span fixé

Dites moi si c’est OK.

Bonjour,

Pas mieux.

Testé sur Chrome et Firefox.

Mince je suis sur Chrome, Ctrl+F5 et :
image
SVP
Sinon click droit sur la valeur (inspecter) et vérifier qu’elle a bien la position absolute sinon problème de cache :
image

J’avais déjà tenté le Ctrl+F5.

C’est bon maintenant.

Bonsoir,

Désolé pour l’attente mais je manque de temps libre.
Voici donc une version mobile sans erreur mais toujours sans couleur :sweat_smile: :

Pour la couleur, le problème vient de la classe highcharts-color-0 qui appartient à Highcharts mais qui est quand même surchargée par le core, je me rappelle m’être bien pris la tête avec ce souci qui était aussi présent sur la version dashboard dans les premières versions de la V4. Je n’ai jamais trouvé de solution simple/propre mais je suis loin de tout connaître.
Je vous propose une solution de bourrin à savoir modifier le code du core.
Sachant que vous êtes développeur, que la classe a été supprimé en v4.4 (preuve qu’elle posait problème) donc ce sera OK quand vous passerez en v4.4 et qu’il n’y aura plus de nouvelle version de la v4.3 (sauf gros bug) donc vous n’aurez qu’à faire cette modification une ou deux fois, je pense que le plus simple est de commenter cette classe en attendant la v4.4 :
image
Si cela ne vous convient pas, il y a un workaround avec des variables CSS proposé par ajja17orange dans le sujet que vous avez cité en lien, ça fait un peu usine à gaz car si je l’intègre il faut revoir la logique que j’ai mis en place avec la 2ème commande mais je veux bien regarder si la méthode bourrin ne vous convient pas.

Pas de souci. Il n’y a pas d’urgence. C’est déjà très sympa d’avoir pris le temps.

Pour la version mobile, je peux attendre la sortie de la 4.4, pas de souci. Je ne suis pas trop fan d’aller modifier le core. Et puis comme annoncé sur le blog, la 4.4 devrait bientôt passer en stable …

1 « J'aime »

Bonjour,
Dans le changelog de la 4.4, il y a :
image
Donc, sans modif il n’y a plus ce widget en 4.4

J’ai ajouté le chargement de solidgauge dans le widget que j’utilise:

<script src="core/php/getJS.php?file=3rdparty/highstock/modules/solid-gauge.js"></script>

Au dessus de la ligne <div class="title #hide_name#">
Ca donne:
image au lieu de image
Les hauteurs et positions des textes passent différemment …

PS: le widget ne s’affiche pas dans un design …

Bonjour,

C’était prévu pour la lib, il faut juste décommenter la ligne :

Quel intérêt de passer par un fichier d’appel plutôt qu’en direct ?
Pour les designs, ça s’affiche au bout d’une minute, la solution est là :

Je n’ai pas encore creusé de mon côté…

Ça sert à faire ce qui est dans core/php/getResource.php appelé par getJS.php
Je pensais que ca empêcherait les multi-chargements du JS mais apparemment ce n’est pas le cas.
Pour un fichier de 5ko, je ne vois pas trop l’intérêt de ce code.
Je modifie.

Par le nom des classes utilisées, je m’aperçois que c’est ton widget modifié à ma sauce que j’utilise pour ma production PV. Faudra que je rentre dans le rang. :wink:

Pour les designs, aller modifier le custom.js pour que le widget fonctionne, ça va pas le faire.
L’affichage au bout d’une minute non plus.