Note: staff-provided content does not represent an official statement from FARGOS Development, LLC. The policy on staff-authored content can be found here.
Go back to Geoff's home page.
Blossom Sprinkler System Controllers were decent cloud-dependent devices that also supported integration with Amazon's Alexa. The original company was acquired by Scotts and the Blossom product line was sunset. At the end of 2021, Scotts turned off the cloud servers that enabled the use of the Blossom controllers, converting them to plastic bricks for most users.
Fortunately, there is an HTTP server built into the Blossom controller that continues to operate despite the inability of the controller to contact the discontinued cloud service. Although their existence was hidden, there are HTTP commands available to manually turn on a watering zone or turn off all watering zones.
A package of scripts and systemd service unit definitons can be downloaded that restores operation of Blossom controllers with the open source Open Sprinkler offering. The respective files are discussed below.
The turnSprinklerValve script below can open valves for a period of time and then close them. It uses wget to issue the relevant requests via HTTP to the Blossom controller.
One complication of the script is that it needs to periodically reissue commands to the Blossom controller because the controller itself will keep attempting to contact the cloud servers that originally supported the product. When these connection attempts fail, the Blossom device halts any ongoing sprinkler activity by closing all valves and starts blinking red LEDs on the device's front cover. A maximum of 5 minutes of sprinkling could be achieved if the open valve directives were not reissued. The refresh interval can be reduced if needed to increase the accuracy of computed total run time.
#!/bin/bash # Open Blossom valve for a period of time # Author: Geoff Carpenter gcc@fargos.net http://www.fargos.net/gcc.html blossomHost="172.28.44.5" # change to local default ledColor=12 valveNum=0 # close inverterFlag=1 duration=0 refreshInterval=10 tmpFile="/dev/null" logFile="/dev/stdout" forceClose=0 busyFile="/tmp/tmp.valveOpen.flag" while test $# -gt 0 do case "${1}" in -h | -help | --help) printf "%s: --host addr --valve id [--inverter mode] [--color code] [--duration seconds] [--refresh interval] [--close] [--output dataFile] [--log fileName]\n" "${0}" >&2 exit 1 ;; --host) blossomHost="${2}" shift ;; --valve) valveNum="${2}" shift ;; --inverter) inverterFlag="${2}" shift ;; --color) ledColor="${2}" shift ;; --duration) duration="${2}" shift ;; --refresh) refreshInterval="${2}" shift ;; --output) tmpFile="${2}" shift ;; --log) logFile="${2}" shift ;; --busy) busyFile="${2}" shift ;; --close) forceClose=1 ;; -*) printf "%s: unrecognized option: \"%s\"\n" "${0}" "${1}" >&2 exit 1 ;; *) printf "%s: unrecognized argument: \"%s\"\n" "${0}" "${1}" >&2 exit 1 ;; esac shift done # Issue open/close valve request to the Blossom sprinkler controller # Note that close requests are done using valve identifier 0, # so closing a valve closes all valves. function turnValve () { valveId=${1:-0} inverterMode=${2:-1} wget --output-document="${tmpFile}" --header="Content-type: application/json" --post-data="{\"valve\":${valveId},\"inverter\":${inverterMode}}" http://${blossomHost}/bloom/valve } function set_LED_sequence () { sequenceCode="${1:-12}" wget --output-document="${tmpFile}" --header="Content-type: application/json" --post-data="{\"red\":0, \"green\":0, \"blue\":0, \"sequence\":${sequenceCode}}" http://${blossomHost}/bloom/led } function forceValveClose () { now=`date '+%Y/%m/%d %H:%M:%S'` echo ${now} $0 force close of valves >> ${logFile} turnValve 0 ${inverterFlag} set_LED_sequence 8 } if test "${forceClose}" != 0 then forceCmd="; forceValveClose" fi trap "rm -f ${busyFile} ${forceCmd}" EXIT now=`date '+%Y/%m/%d %H:%M:%S'` echo ${now} $0 turn valve ${valveNum} >> ${logFile} echo ${now} $0 turn valve ${valveNum} >> ${busyFile} set_LED_sequence ${ledColor} turnValve ${valveNum} ${inverterFlag} startTime=`date +%s` desiredEndTime=`expr ${startTime} + ${duration}` curTime="${startTime}" while test ${curTime} -lt ${desiredEndTime} do sleep ${refreshInterval} now=`date '+%Y/%m/%d %H:%M:%S'` echo ${now} $0 refresh valve ${valveNum} >> ${logFile} set_LED_sequence ${ledColor} turnValve ${valveNum} ${inverterFlag} curTime=`date +%s` done exit 0
While the above script can be used to manually turn on a watering zone for a period of time, it lacks all of the expected automation. The open source Open Sprinkler project provides a very flexible foundation that replaces the no-longer-supported Blossom app. The Open Sprinkler server (aka, "Unified Firmware") supports a variety of interfaces to controllers and valves, one of which is remote control via HTTP requests. The doSprinklerRequests script below acts as a long-running daemon that accepts requests from Open Sprinkler and issues appropriate requests to the Blossom controller.
#!/bin/bash # USAGE: doSprinklerRequests [blossomAddress [listenPort]] # Interface to enable access by Open Sprinkler to no-longer-supported # (as of 2022) Blossom sprinkler controller hardware. # Author: Geoff Carpenter gcc@fargos.net http://www.fargos.net/gcc.html # # Accepts from Open Sprinkler HTTP requests of the form: # GET /valve/${valveNum}/open and # GET /valve/${valveNum}/close # # LOCAL CONFIGURATION/CUSTOMIZATION # NOTE: blossomHost must be set correctly to address of the Blossom # controller. This can be the name of the controller if your local # DHCP server knows it; otherwise (and most reliably), it should be # the statically assigned IP address. blossomHost="${1:-172.28.44.5}" # MUST be set to address of Blossom controller listenPort="${2:-8081}" debugLevel=0 waitingColor=4 # choose from set_LED_sequence options below wateringColor=12 # choose from set_LED_sequence options below disabledColor=8 # choose from set_LED_sequence options below maxDuration=1800 # maximum possible continuous valve open time # Stations need to be configured in Open Sprinkler as HTTP remote stations. # To do this from the Open Sprinkler GUI, go to the Advanced tab of the # station settings: # Station Type: HTTP # Server Name: 127.0.0.1 # Server Port: 8081 (or what listenPort is set to below) # On Command: valve/1/on # Off Command: valve/1/off # # NOTE: commands do not begin with a "/" as the Open Sprinkler firmware # always prepends a "/" to the GET request. originDir=`dirname "${0}"` logFile=/tmp/tmp.`basename $0`.$$ # set to /dev/null to ignore output latestLog=/tmp/sprinklerRequests.log tmpFile=/tmp/tmp.valveRequest.$$ busyFile=/tmp/tmp.busy.$$ # Issue open/close valve request to the Blossom sprinkler controller # Note that close requests are done using valve identifier 0, # so closing a valve closes all valves. function turnValve () { valveId=${1:-0} inverterMode=${2:-1} wget --output-document="${tmpFile}" --header="Content-type: application/json" --post-data="{\"valve\":${valveId},\"inverter\":${inverterMode}}" http://${blossomHost}/bloom/valve } # 1=Pulsing Blue/Green # 2=Blinking Red # 3=Fixed Blue # 4=Slowly Pulsing Blue # 5=Fixed Blue (Watering 1) # 6=Fixed Blue/Green (Watering 2) # 7=Pulsing Pink # 8=Fast Blinking Pink # 9=Fixed orange # 10=Pulsing Red # 11=Fast Blinking Blue/Green # 12=Fast Blinking purple # 13=Fixed Purple function set_LED_sequence () { sequenceCode="${1:-${waitingColor}}" wget --output-document="${tmpFile}" --header="Content-type: application/json" --post-data="{\"red\":0, \"green\":0, \"blue\":0, \"sequence\":${sequenceCode}}" http://${blossomHost}/bloom/led } # Set custom LED color on Blossom box as RGB value function set_LED_RGB () { red="${1:-0}" green="${2:-0}" blue="${3:-0}" wget --output-document="${tmpFile}" --header="Content-type: application/json" --post-data="{\"red\":${red}, \"green\":${green}, \"blue\":${blue}, \"sequence\":0}" http://${blossomHost}/bloom/led } now=`date '+%Y/%m/%d %H:%M:%S'` echo ${now} $0 begins operation >> ${logFile} # if output is to regular file, make link to common persistent name if test -f "${logFile}" then # Use hard link for common log file name rm -f "${latestLog}" ln "${logFile}" "${latestLog}" logFileToRemove="${logFile}" fi nice ${originDir}/refreshBlossomColor ${blossomHost} $$ ${busyFile} ${waitingColor} ${disabledColor} >& /dev/null & refreshPID=$! # Force valve close on exit, indicate not operating trap "turnValve 0 1; set_LED_sequence ${disabledColor}; rm -f ${tmpFile} ${logFileToRemove} ${busyFile}" EXIT openTime=0 lastOpenPID="" while true do now=`date '+%Y/%m/%d %H:%M:%S'` echo ${now} listen on ${listenPort} >> ${logFile} set_LED_sequence ${waitingColor} # waiting nc -l -k -p ${listenPort} | while read cmd arg proto everything do now=`date '+%Y/%m/%d %H:%M:%S'` if test ${debugLevel} -gt 0 then echo ${now} cmd ${cmd} arg ${arg} >> ${logFile} if test ${debugLevel} -gt 1 then echo ${now} proto ${proto} remainder ${everything} >> ${logFile} fi fi case X"${cmd}" in XGET) valveNum=`echo ${arg} | cut -d / -f 3` openOrClose=`echo ${arg} | cut -d / -f 4` optionalInverterMode=`echo ${arg} | cut -d / -f 5` mode=${optionalInverterMode:-1} case X"${openOrClose}" in Xon) openTime=`date '+%s'` echo ${now} ${openTime} Open valve ${valveNum} >> ${logFile} echo ${now} ${openTime} Open valve ${valveNum} >> ${busyFile} ${originDir}/turnSprinklerValve --host ${blossomHost} --valve ${valveNum} --inverter ${mode} --color ${wateringColor} --duration ${maxDuration} --output ${tmpFile} --log ${logFile} --busy ${busyFile} --close & lastOpenPID=$! ;; Xoff | 'X*') # Blossom uses valve 0 to close closeTime=`date +%s` duration=`expr ${closeTime} - ${openTime}` echo ${now} ${closeTime} Close valve ${valveNum} after ${duration} seconds >> ${logFile} if test -n "${lastOpenPID}" then kill ${lastOpenPID} wait lastOpenPID="" fi turnValve 0 ${mode} set_LED_sequence ${waitingColor} rm -f ${busyFile} ;; esac ;; XHOST) ;; X) ;; *) ;; esac done now=`date +'%Y/%m/%d %H:%M:%S'` echo ${now} connection from Open Sprinkler closed >> ${logFile} done
As noted previously, the Blossom controller periodically attempts to connect to the defunct cloud services that once supported its operation. The inevitable failure to success leads to flashing red LEDS being presented on the Blossom case and closing of any open valves. The refreshBlossomColor script periodically resets the LED status to indicate that the Blossom device is being controlled by doSprinklerRequets.
#!/bin/bash # Periodicallly refresh color of status LEDs on Blossom controller # Blossom firmware will periodically reattempt connection to the # defunct cloud services, fail, and then turn the LEDs red. # refreshBlossomColor reinjects the desired color mode while its # parent doSprinklerRequests script waits for requests. # Author: Geoff Carpenter gcc@fargos.net http://www.fargos.net/gcc.html refreshInterval=10 myPID=$$ blossomHost="${1}" parentPID="${2}" monitorFile="${3}" waitingColor=${4:-4} # choose from set_LED_sequence options disabledColor=${5:-8} # choose from set_LED_sequence options tmpFile="/tmp/tmp.blossomRefresh.$$" trap "rm -f ${tmpFile}" EXIT function set_LED_sequence () { sequenceCode="${1:-${waitingColor}}" wget --output-document="${tmpFile}" --header="Content-type: application/json" --post-data="{\"red\":0, \"green\":0, \"blue\":0, \"sequence\":${sequenceCode}}" http://${blossomHost}/bloom/led } kill -0 ${parentPID} parentGone=$? while test ${parentGone} -eq 0 do if test ! -f "${monitorFile}" then set_LED_sequence ${waitingColor} fi sleep ${refreshInterval} kill -0 ${parentPID} parentGone=$? done set_LED_sequence ${disabledColor}
The following blossom.service prototype service unit definition will work with systemd to launch the Blossom gateway. Substitute "{sprinkler}" with the user name used to build the software. Substitute "blossomAddress" with the IP address of the Blossom controller.
[Unit] Description=Blossom gateway for Open Sprinkler [Service] User={sprinkler} ExecStart=/home/{sprinkler}/doSprinklerRequests blossomAddress WorkingDirectory=/home/{sprinkler} Restart=always RestartSec=10 [Install] WantedBy=multi-user.target
It is not strictly necessary to use a local weather service daemon, but doing so insulates one from depending on the Open Sprinkler team continuing to provide such services on behalf of the public. Note that Open Sprinkler defaulted to using Dark Sky as a source for weather data, but Apple's acquisition of the service has resulted in support for the API being turned off in March of 2023. The Open Sprinkler weather service can be configured to use Open Weather as an alternative and this was recommended practice as of 2022.
The following weather.service provides a service unit definition for the Open Sprinkler weather daemon. Substitute "{sprinkler}" with the user name used to build the software.
[Unit] Description=OpenSprinkler Weather Server [Service] User={sprinkler} ExecStart=/usr/bin/npm start WorkingDirectory=/home/{sprinkler}/OpenSprinkler-Weather Restart=always RestartSec=10 [Install] WantedBy=multi-user.target
The following sprinkler.service provides a service unit definition for the Open Sprinkler daemon, referred to as the "unified firmware". Substitute "{sprinkler}" with the user name used to build the software.
[Unit] Description=Open Sprinkler Server Wants=weather.service Requires=blossom.service [Service] User={sprinkler} ExecStart=/home/{sprinkler}/OpenSprinkler-Firmware/OpenSprinkler WorkingDirectory=/home/{sprinkler}/OpenSprinkler-Firmware Restart=always RestartSec=10 [Install] WantedBy=multi-user.target