Adding support for Seneye Aquarium & Pond Sensors (removing the need for a Seneye Web Server)

@cowboy details of the LDE format can be found on the github site listed

Which suggests that it’s the same format as the SCA sends

“This repository contains documentation explaining how to enable the LDE, as well as documented example code that demonstrates methods for handling LDE requests sent by your SWS or SCA.”

Thanks @GadgetUK, but the SCA app wasn’t what I was referring too.

In addition to the SCA app and the SWS device, both which feature LDE, there was another way.

Seneye also makes (or used to make) data retrieval possible via ‘api.seneye.com’.

This is the method I used to use, for a few reasons.
1.) Until mcclown had released pyseneye, I had not updated my Seneye firmwares to version 2.
2.) I also had not upgraded my SCA app to the newest version, knowing full well that it would force my Seneye devices to version 2. There was no LDE in this original SCA version.
3.) I had not done this because in version 2, Seneye introduced artificial and very enforced time limits on how long one could use their slides beyond the 30 day period (a grace period of 10 days, then they filter out your data for pH and Ammonia, even though the slides would continue to actually work longer than that. Granted, the margin of error would slowly increase over time, but it was predictable and stable, and thus usable for less critical uses.)
4.) This kinda bullshit … vendors introducing artificial restrictions on how one can use the devices they own… really pisses me off. :slight_smile:

The API at Seneye’s own servers may return different data formats between v1 and v2 devices. Certainly the URL request was different depending on v1 or v2 firmwares. V1 firmwares could use a API request like this:

https://api.seneye.com/v1/devices/[DEVICEID]?IncludeState=1&user=[USER-LOGIN]&pwd=[USER-PASSWORD] 

I’d have to spend some time investigating how different API requests and responses between V1 and V2 devices are. But that much time I don’t have at the moment due to my client work demands at the moment.

But if you and/or @Casper_Bours want to take my V1 script and use that as a starting point to bring LDE support to the party, your welcome to do so. I just ask that if successful, the version 2 of the script you come up with, that it’s shared back to this thread AND/OR this thread ( https://community.home-assistant.io/t/going-to-next-level-of-aquarium-automation-whos-with-me/ ) for others to use. I’m attaching my v1 of the script to this post. :slight_smile:

Quick explanation:
It’s a bash script that needs to be executed by the crontab of the home assistant account (not root) approx every 10-15 minutes. It makes a request to the API server and then parses the XML data using grep, sed, etc out into individual files that HA can import them easily from. It’s dirty, but it worked reliably for me for more than a year.

It creates a folder which is named after the Seneye Device name and inside that folder are lots of little files which contain the measurements. Just setup HA to use those individual files as file based sensors and that’s it. Further details are in the comments of the bash script itself.

#!/bin/sh
# Seneye API Simple Sensor Parser - Copyright 2017 Kevin McPeake
# A simple web API request function and parser for retrieving Seneye Device Data readings from the Seneye API Server, and parsing them to static file locations for reading
# and processing of data by Home Assistant's Command Line Sensor function.  Just a quickly hacked up workaround for starting to get Seneye Data into Home Assistant.
#
# Instructions for installing and using.
# 1) Save this script to a file named 'seneye' for a single seneye device, or if multiple seneye devices are used, make individual copies of this file based on a naming convention
#    like "seneye-1, seneye-2, seneye-3" or "Seneye-reef, Seneye-Pond, Seneye-Fresh" or something similiar like that.
# 2) Save / copy this file to a directory where you'll be sure to include it in your backups of Home Assistant and Home Assistant's user has access rights to read and execute.  This
#    could be in /usr/bin or /usr/local/bin or ~/bin ... whatever works best for you.  It should only be writeable by root. And it's probably BEST if it's only readable and executable by
#    Home Assistant user account and nothing else, since it will be storing your Seneye Password details.
# 3) modify the 'sensor=' variable declaration to reflect the name of your Seneye.  I recommend if you have multiple sensors and copies of this executable script, each named
#    as described / suggested above (i.e.- Seneye-Reef, Seneye-Pond, etc) that you set this to reflect the suffix of that file name for consistancy. (i.e.- sensor=Reef ).
# 4) Change the [DEVICEID], the [USER-LOGIN] and [USER-PASSWORD] to reflect your true Seneye settings;
#     a) [DEVICEID] can be discovered using the Seneye Web Server or the Seneye Connect Application (Windows).  Go to "Device Info" and you can find the nummerical
#        DeviceID number listed there.
#     b) [USER-LOGIN] and [USER-PASSWORD] also should be changed to reflect the login details provided when you login to the Seneye Cloud Portal.
# 5) Run the script (or scripts) as the Home Assistent user that HA runs under to ensure it is working properly.  Check within the directory of ~homeassistant/seneye/[SENSOR-NAME]
#    You should see a listing of files all reflecting the filename for the values they contain.  If you configured multiple sensors correctly, you should have a different folder
#    within the 'seneye' folder, each reflecting the name you gave that sensor in the script, each folder containing the respective values for that sensor.
# 6) Add all the seneye scripts you created to a crontab entry that is set to execute every 10-15 minutes.
# 7) The files within the folder structure will update at the specified crontab intervals. Now all you have to do is create the appropriate 'Command Line Sensors' in Home Assistant
#    that reads the contents of the files.  An example follows:
#
#    sensor:
#      - platform: command_line
#        command: "cat /home/homeassistant/seneye/reef/temperature_curr"
#        name: Reef Temp
#        unit_of_measurement: C
#      - platform: command_line
#        command: "cat /home/homeassistant/seneye/reef/ph_curr"
#        name: Reef PH
#        unit_of_measurement: Ph
#      - platform: command_line
#        command: "cat /home/homeassistant/seneye/reef/nh3_curr"
#        name: Reef NH3
#        unit_of_measurement: ppm
#      - platform: command_line
#        command: "cat /home/homeassistant/seneye/reef/nh4_curr"
#        name: Reef NH4
#        unit_of_measurement: ppm
#      - platform: command_line
#        command: "cat /home/homeassistant/seneye/reef/o2_curr"
#        name: Reef O2
#        unit_of_measurement: ppm
#      - platform: command_line
#        command: "cat /home/homeassistant/seneye/reef/slide_expires"
#        name: Reef Slide Expires
#      - platform: command_line
#        command: "cat /home/homeassistant/seneye/reef/slide_serial"
#        name: Reef Serial
#      - platform: command_line
#        command: "cat /home/homeassistant/seneye/reef/last_experiment"
#        name: Reef Last Test
#
# 8) And away you go.
#
# Additional notes:  Seneye claims their API server calls are Rate Limited.  What this Rate Limit is, I don't know (yet) and I didn't want Home Assistant to end up blacklisting
# my account because Home Assistant's restAPI Sensor function was calling (read: pounding) the Seneye API server relentlessly.   Further to that, Seneye's SCA and SWS solutions for
# uploading the Seneye data to the Seneye webservers only does this once every 30 minutes anyways.  So HA's relentlessly pounding on their webserver asking for updates every 30-60 seconds
# probably wouldn't be appreciated on either ends of the links - mine or theirs.
# As a temp solution, I decided to quickly jot out this simple bash script. I wrote it in Bash because I could actually do that faster than in Python, however, I may go back and re-write a
# python implementation.  If someone else feels up to the job, please be my guest. :D
# For now, this was a quick workaround and wasn't my final stopping point.  My next real goal is to completely replace the SCA / SWS requirement & try to get a working USB based implementation
# of reading the Seneye data right off the Seneye. Someone else has already begun this, but it's not stable or long term reliable just yet.  Hopefully we can fix that in the coming weeks and months.
# But until then, this is a good workaround if one already has a SWS or SCA and would like to have a copy of the Seneye data imported into HA, without having to run another local isntall
# of NodeJS for Seneye Data Exchange.
#
###################

# Change the name of individual sensor here.  This can be whatever you want. (Be sure to remove the "[" and "]" brackets from your entry.)
sensor=[SENSOR NAME]
mkdir -p ~/seneye/$sensor

# change [DEVICEID] and [userlogin] / [password] and be sure to remove the "[" and "]" from the URL string (they aren't needed) in the line below
#
wget --header='Accept:application/xml' "https://api.seneye.com/v1/devices/[DEVICEID]?IncludeState=1&user=[USER-LOGIN]&pwd=[USER-PASSWORD]" -O - > ~/seneye/$sensor/$sensor.xml


# Leave the rest of this alone, modification shouldn't be needed here.


grep description ~/seneye/$sensor/$sensor.xml | sed 's/^<response><id>....<\/id><description>//g' | sed 's/<\/description>.*$//g' > ~/seneye/$sensor/description
grep description ~/seneye/$sensor/$sensor.xml | sed 's/^<response><id>.*<disconnected>//g' | sed 's/<\/disconnected>.*$//g' > ~/seneye/$sensor/disconnected
grep description ~/seneye/$sensor/$sensor.xml | sed 's/^<response><id>.*<slide_serial>//g' | sed 's/<\/slide_serial>.*$//g' > ~/seneye/$sensor/slide_serial
date -d @`grep description ~/seneye/$sensor/$sensor.xml | sed 's/^<response><id>.*<slide_expires>//g' | sed 's/<\/slide_expires>.*$//g'` > ~/seneye/$sensor/slide_expires
grep description ~/seneye/$sensor/$sensor.xml | sed 's/^<response><id>.*<out_of_water>//g' | sed 's/<\/out_of_water>.*$//g' > ~/seneye/$sensor/out_of_water
grep description ~/seneye/$sensor/$sensor.xml | sed 's/^<response><id>.*<wrong_slide>//g' | sed 's/<\/wrong_slide>.*$//g' > ~/seneye/$sensor/wrong_slide
date -d @`grep description ~/seneye/$sensor/$sensor.xml | sed 's/^<response><id>.*<last_experiment>//g' | sed 's/<\/last_experiment>.*$//g'` > ~/seneye/$sensor/last_experiment

grep description ~/seneye/$sensor/$sensor.xml | sed 's/^<response><id>.*<temperature>//g' | sed 's/<\/temperature>.*$//g' | sed 's/^<trend>//g' | sed 's/<\/trend>.*$//g' > ~/seneye/$sensor/temperature_trend
grep description ~/seneye/$sensor/$sensor.xml | sed 's/^<response><id>.*<temperature>//g' | sed 's/<\/temperature>.*$//g' | sed 's/^.*<critical_in>//g' | sed 's/<\/critical_in>.*$//g' > ~/seneye/$sensor/temperature_critical_in
grep description ~/seneye/$sensor/$sensor.xml | sed 's/^<response><id>.*<temperature>//g' | sed 's/<\/temperature>.*$//g' | sed 's/^.*<avg>//g' | sed 's/<\/avg>.*$//g' > ~/seneye/$sensor/temperature_avg
grep description ~/seneye/$sensor/$sensor.xml | sed 's/^<response><id>.*<temperature>//g' | sed 's/<\/temperature>.*$//g' | sed 's/^.*<status>//g' | sed 's/<\/status>.*$//g' > ~/seneye/$sensor/temperature_status
grep description ~/seneye/$sensor/$sensor.xml | sed 's/^<response><id>.*<temperature>//g' | sed 's/<\/temperature>.*$//g' | sed 's/^.*<curr>//g' | sed 's/<\/curr>.*$//g' > ~/seneye/$sensor/temperature_curr

grep description ~/seneye/$sensor/$sensor.xml | sed 's/^<response><id>.*<ph>//g' | sed 's/<\/ph>.*$//g' | sed 's/^<trend>//g' | sed 's/<\/trend>.*$//g' > ~/seneye/$sensor/ph_trend
grep description ~/seneye/$sensor/$sensor.xml | sed 's/^<response><id>.*<ph>//g' | sed 's/<\/ph>.*$//g' | sed 's/^.*<critical_in>//g' | sed 's/<\/critical_in>.*$//g' > ~/seneye/$sensor/ph_critical_in
grep description ~/seneye/$sensor/$sensor.xml | sed 's/^<response><id>.*<ph>//g' | sed 's/<\/ph>.*$//g' | sed 's/^.*<avg>//g' | sed 's/<\/avg>.*$//g' > ~/seneye/$sensor/ph_avg
grep description ~/seneye/$sensor/$sensor.xml | sed 's/^<response><id>.*<ph>//g' | sed 's/<\/ph>.*$//g' | sed 's/^.*<status>//g' | sed 's/<\/status>.*$//g' > ~/seneye/$sensor/ph_status
grep description ~/seneye/$sensor/$sensor.xml | sed 's/^<response><id>.*<ph>//g' | sed 's/<\/ph>.*$//g' | sed 's/^.*<curr>//g' | sed 's/<\/curr>.*$//g' > ~/seneye/$sensor/ph_curr

grep description ~/seneye/$sensor/$sensor.xml | sed 's/^<response><id>.*<nh3>//g' | sed 's/<\/nh3>.*$//g' | sed 's/^<trend>//g' | sed 's/<\/trend>.*$//g' > ~/seneye/$sensor/nh3_trend
grep description ~/seneye/$sensor/$sensor.xml | sed 's/^<response><id>.*<nh3>//g' | sed 's/<\/nh3>.*$//g' | sed 's/^.*<critical_in>//g' | sed 's/<\/critical_in>.*$//g' > ~/seneye/$sensor/nh3_critical_in
grep description ~/seneye/$sensor/$sensor.xml | sed 's/^<response><id>.*<nh3>//g' | sed 's/<\/nh3>.*$//g' | sed 's/^.*<avg>//g' | sed 's/<\/avg>.*$//g' > ~/seneye/$sensor/nh3_avg
grep description ~/seneye/$sensor/$sensor.xml | sed 's/^<response><id>.*<nh3>//g' | sed 's/<\/nh3>.*$//g' | sed 's/^.*<status>//g' | sed 's/<\/status>.*$//g' > ~/seneye/$sensor/nh3_status
grep description ~/seneye/$sensor/$sensor.xml | sed 's/^<response><id>.*<nh3>//g' | sed 's/<\/nh3>.*$//g' | sed 's/^.*<curr>//g' | sed 's/<\/curr>.*$//g' > ~/seneye/$sensor/nh3_curr

grep description ~/seneye/$sensor/$sensor.xml | sed 's/^<response><id>.*<nh4>//g' | sed 's/<\/nh4>.*$//g' | sed 's/^<trend>//g' | sed 's/<\/trend>.*$//g' > ~/seneye/$sensor/nh4_trend
grep description ~/seneye/$sensor/$sensor.xml | sed 's/^<response><id>.*<nh4>//g' | sed 's/<\/nh4>.*$//g' | sed 's/^.*<critical_in>//g' | sed 's/<\/critical_in>.*$//g' > ~/seneye/$sensor/nh4_critical_in
grep description ~/seneye/$sensor/$sensor.xml | sed 's/^<response><id>.*<nh4>//g' | sed 's/<\/nh4>.*$//g' | sed 's/^.*<avg>//g' | sed 's/<\/avg>.*$//g' > ~/seneye/$sensor/nh4_avg
grep description ~/seneye/$sensor/$sensor.xml | sed 's/^<response><id>.*<nh4>//g' | sed 's/<\/nh4>.*$//g' | sed 's/^.*<status>//g' | sed 's/<\/status>.*$//g' > ~/seneye/$sensor/nh4_status
grep description ~/seneye/$sensor/$sensor.xml | sed 's/^<response><id>.*<nh4>//g' | sed 's/<\/nh4>.*$//g' | sed 's/^.*<curr>//g' | sed 's/<\/curr>.*$//g' > ~/seneye/$sensor/nh4_curr

grep description ~/seneye/$sensor/$sensor.xml | sed 's/^<response><id>.*<o2>//g' | sed 's/<\/o2>.*$//g' | sed 's/^<trend>//g' | sed 's/<\/trend>.*$//g' > ~/seneye/$sensor/o2_trend
grep description ~/seneye/$sensor/$sensor.xml | sed 's/^<response><id>.*<o2>//g' | sed 's/<\/o2>.*$//g' | sed 's/^.*<critical_in>//g' | sed 's/<\/critical_in>.*$//g' > ~/seneye/$sensor/o2_critical_in
grep description ~/seneye/$sensor/$sensor.xml | sed 's/^<response><id>.*<o2>//g' | sed 's/<\/o2>.*$//g' | sed 's/^.*<avg>//g' | sed 's/<\/avg>.*$//g' > ~/seneye/$sensor/o2_avg
grep description ~/seneye/$sensor/$sensor.xml | sed 's/^<response><id>.*<o2>//g' | sed 's/<\/o2>.*$//g' | sed 's/^.*<status>//g' | sed 's/<\/status>.*$//g' > ~/seneye/$sensor/o2_status
grep description ~/seneye/$sensor/$sensor.xml | sed 's/^<response><id>.*<o2>//g' | sed 's/<\/o2>.*$//g' | sed 's/^.*<curr>//g' | sed 's/<\/curr>.*$//g' > ~/seneye/$sensor/o2_curr

grep description ~/seneye/$sensor/$sensor.xml | sed 's/^<response><id>.*<lux>//g' | sed 's/<\/lux>.*$//g' | sed 's/^.*<curr>//g' | sed 's/<\/curr>.*$//g' > ~/seneye/$sensor/lux_curr
grep description ~/seneye/$sensor/$sensor.xml | sed 's/^<response><id>.*<par>//g' | sed 's/<\/par>.*$//g' | sed 's/^<curr>//g' | sed 's/<\/curr>.*$//g' > ~/seneye/$sensor/par_curr
grep description ~/seneye/$sensor/$sensor.xml | sed 's/^<response><id>.*<kelvin>//g' | sed 's/<\/kelvin>.*$//g' | sed 's/^<curr>//g' | sed 's/<\/curr>.*$//g' > ~/seneye/$sensor/kelvin_curr

Example of the HA account crontab should look something like this. In my case, I have 3 Seneye devices, so I had 3 scripts to execute, each customised for a specific Seneye device.

00,30   *       *       *       *       /usr/local/bin/seneye-1
00,30   *       *       *       *       /usr/local/bin/seneye-2
00,30   *       *       *       *       /usr/local/bin/seneye-3

Hi Guys. Has anyone got this working on Hassio? Ive invested a fair bit of time on my current Hassio setup so would like to get it working on Hassio if possible. Previously i have copied the py files into custom_components folder to get around the pip install issue but the sensor still does not show when i do this.

Hey, what version are you using? I want to add this to HACS in the next few weeks, so that it’s easier to install. I have it working with a pretty recent version at home though, so it should still be working.

Any errors in the log?

Hey. All i seem to get is.

“Platform error: sensor - Integration ‘seneye’ not found.”

I am trying to follow these steps but dont seem to be getting anywhere.

Did you use the latest code from GitHub? https://github.com/mcclown/home-assistant-custom-components

What version of hass.io are you using? I use hass.io at home as well, so I do test it with hass.io.

Hi. I am using 0.101.2. I copied the most recent seneye folder into my custom components folder. Can you confirm how you installed the sensor in hassio?

I’m on 0.100.3 so there shouldn’t be any major differences. I just copied the folder over as well. Here’s how it looks for me:

Do you have other custom components working?

That is how my folder structure looks. I have icloud3 working fine. Did you have to do anything to get the pyseneye module to install the usb drivers?

Ok after creating the seneye folder and copying the files into it (rather than taking the whole folder) i now get a warning that i am using a custom component. This is a step forward.
I am now getting this error.

Platform seneye not ready yet. Retrying in 60 seconds.
7:55 PM helpers/entity_platform.py (WARNING)
Error: Device not found
7:55 PM custom_components/seneye/sensor.py (ERROR) - message first occurred at 7:55 PM and shows up 2 times

Maybe the previous issue was a permission problem, for the folder.

This is more interesting. Is your Seneye firmware up to date? There shouldn’t be any driver required. It uses a generic driver so all that should be required is adding it to the custom component folder. Are you using HASS on a Raspberry Pi, or what is the system?

Hi yes I’m running hassio on a raspberry pi. I’ll see if I can update the seneye firmware and see how I get on. Thanks.

Did you get it working finally, @aaron_ridgewell? If so, how’s it working out for you?

Hey @mcclown, have you any idea how to make your integration work with more than 1 x Seneye sensor attached to the same RaspPi? I found my 3rd Seneye Sensor (got swept away in a box) and have hooked it up to my fish room located RPI, but upon reboot and restart of HA, HA only still sees the first Seneye sensor, not the second one. Using "uhubctl"d I can see both Seneye devices, but HA can’t see the second device.

Thanks in advance

Hi, any ideas why I see this in the logs?
Using hassio with the latest component from GIT
thanks

Logger: homeassistant.components.sensor
Source: helpers/entity_platform.py:202
Integration: Sensor (documentation, issues)
First occurred: 16:55:39 (4 occurrences)
Last logged: 17:00:12

  • Platform seneye not ready yet. Retrying in 60 seconds.
  • Platform seneye not ready yet. Retrying in 90 seconds.
  • Platform seneye not ready yet. Retrying in 120 seconds.
  • Platform seneye not ready yet. Retrying in 150 seconds.

Log Details (ERROR)

Logger: custom_components.seneye.sensor
Source: custom_components/seneye/sensor.py:69
Integration: seneye (documentation)
First occurred: 16:55:07 (14 occurrences)
Last logged: 17:26:51

Error: Device not found

I have this working now, the slide needed registering :slight_smile:
Thank you!

Will be following and trying to implement this in the next coming weeks aswell on my reefer 425xl.
I do have a question I have currently hass io running nog my raspi 3.
Now before i read this thread I was planning to buy an always on PC to run seneye.

But what is best now ? what would you recommend ?
I am thinking to buy windows always on PC and then run both Seneye & hass.io on same device so I can experiment but worst case run seneye in windows again.
As for using the API from seneye I guess you then need SWS or windows pc aswel right ?

Hello guys, just following up on any updates to this component. I installed it in haas a few weeks ago and it has been working ever since. looking at the hass component code ‘sensor.py’ i can see device classes as such :
‘’’
DEVICE_CLASS_PH = ‘PH’
DEVICE_CLASS_FREE_AMMONIA = ‘NH3’
‘’’
are lux/par/pur device(s) not available in class definition for that data to be read in the same manner? Another thing that i noticed was 30 minute test interval with 5 minute update/scan interval. So i went ahead changed my test interval (slide read) to 60 minutes for once every hour then in
‘’’
@property def available(self):
‘’’
changed last_api_data to be late 3 times the value of last throttle which would be 60 mins with above change. I am hoping to extend run time for each slide (hoping).

I enjoy reading your article and really want to try it out. But with my limited python knowledge, I am wondering if this can be implemented in HASSIO what I’m currently running. Or do I have to build bare OS, install HA and then add your custom component? Thanks in advance.